Commit cad508def69c402efe0b6d7179eeabd6d572d630
1 parent
ca4afc29
mall + pay:
1. bar 扫码支付成功后,额外返回 notify 2. notify 在支付回调时,增加幂等处理
Showing
8 changed files
with
82 additions
and
54 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/order/PayOrderUnifiedRespDTO.java
| 1 | package cn.iocoder.yudao.framework.pay.core.client.dto.order; | 1 | package cn.iocoder.yudao.framework.pay.core.client.dto.order; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; | ||
| 3 | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; | 4 | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; |
| 4 | -import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; | ||
| 5 | import lombok.Data; | 5 | import lombok.Data; |
| 6 | 6 | ||
| 7 | /** | 7 | /** |
| @@ -24,19 +24,15 @@ public class PayOrderUnifiedRespDTO { | @@ -24,19 +24,15 @@ public class PayOrderUnifiedRespDTO { | ||
| 24 | private String displayContent; | 24 | private String displayContent; |
| 25 | 25 | ||
| 26 | /** | 26 | /** |
| 27 | - * 支付状态 | 27 | + * 同步的通知信息 |
| 28 | * | 28 | * |
| 29 | - * 枚举 {@link PayOrderStatusRespEnum} 类 | 29 | + * 目前只有 bar 条码支付才会出现,它是支付发起时,直接返回是否支付成功的,而其它支付还是异步通知 |
| 30 | */ | 30 | */ |
| 31 | - private Integer status; | 31 | + private PayOrderNotifyRespDTO notify; |
| 32 | 32 | ||
| 33 | public PayOrderUnifiedRespDTO(String displayMode, String displayContent) { | 33 | public PayOrderUnifiedRespDTO(String displayMode, String displayContent) { |
| 34 | - this(displayMode, displayContent, PayOrderStatusRespEnum.WAITING.getStatus()); | ||
| 35 | - } | ||
| 36 | - | ||
| 37 | - public PayOrderUnifiedRespDTO(String displayMode, String displayContent, Integer status) { | ||
| 38 | this.displayMode = displayMode; | 34 | this.displayMode = displayMode; |
| 39 | this.displayContent = displayContent; | 35 | this.displayContent = displayContent; |
| 40 | - this.status = status; | ||
| 41 | } | 36 | } |
| 37 | + | ||
| 42 | } | 38 | } |
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; | @@ -5,13 +5,13 @@ import cn.hutool.core.thread.ThreadUtil; | ||
| 5 | import cn.hutool.core.util.StrUtil; | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; | 6 | import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; |
| 7 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 7 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 8 | +import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; | ||
| 8 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; | 9 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; |
| 9 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; | 10 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; |
| 10 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; | 11 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; |
| 11 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; | 12 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; |
| 12 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; | 13 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; |
| 13 | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; | 14 | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; |
| 14 | -import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; | ||
| 15 | import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest; | 15 | import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest; |
| 16 | import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult; | 16 | import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult; |
| 17 | import com.github.binarywang.wxpay.constant.WxPayConstants; | 17 | import com.github.binarywang.wxpay.constant.WxPayConstants; |
| @@ -70,9 +70,15 @@ public class WxBarPayClient extends AbstractWxPayClient { | @@ -70,9 +70,15 @@ public class WxBarPayClient extends AbstractWxPayClient { | ||
| 70 | try { | 70 | try { |
| 71 | WxPayMicropayResult response = client.micropay(request); | 71 | WxPayMicropayResult response = client.micropay(request); |
| 72 | // 支付成功(例如说,用户输入了密码) | 72 | // 支付成功(例如说,用户输入了密码) |
| 73 | + PayOrderNotifyRespDTO notify = PayOrderNotifyRespDTO.builder() | ||
| 74 | + .orderExtensionNo(response.getOutTradeNo()) | ||
| 75 | + .channelOrderNo(response.getTransactionId()) | ||
| 76 | + .channelUserId(response.getOpenid()) | ||
| 77 | + .successTime(parseDateV2(response.getTimeEnd())) | ||
| 78 | + .build(); | ||
| 73 | return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.BAR_CODE.getMode(), | 79 | return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.BAR_CODE.getMode(), |
| 74 | - JsonUtils.toJsonString(response), | ||
| 75 | - PayOrderStatusRespEnum.SUCCESS.getStatus()); | 80 | + JsonUtils.toJsonString(response)) |
| 81 | + .setNotify(notify); | ||
| 76 | } catch (WxPayException ex) { | 82 | } catch (WxPayException ex) { |
| 77 | // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理 | 83 | // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理 |
| 78 | // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。 | 84 | // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。 |
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 { | @@ -25,6 +25,7 @@ public interface ErrorCodeConstants { | ||
| 25 | ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(1007002000, "支付订单不存在"); | 25 | ErrorCode PAY_ORDER_NOT_FOUND = new ErrorCode(1007002000, "支付订单不存在"); |
| 26 | ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付"); | 26 | ErrorCode PAY_ORDER_STATUS_IS_NOT_WAITING = new ErrorCode(1007002001, "支付订单不处于待支付"); |
| 27 | ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付"); | 27 | ErrorCode PAY_ORDER_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007002002, "支付订单不处于已支付"); |
| 28 | + ErrorCode PAY_ORDER_IS_EXPIRED = new ErrorCode(1007002003, "支付订单已经过期"); | ||
| 28 | 29 | ||
| 29 | // ========== ORDER 模块(拓展单) 1007003000 ========== | 30 | // ========== ORDER 模块(拓展单) 1007003000 ========== |
| 30 | ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在"); | 31 | ErrorCode PAY_ORDER_EXTENSION_NOT_FOUND = new ErrorCode(1007003000, "支付交易拓展单不存在"); |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderSubmitRespVO.java
| 1 | package cn.iocoder.yudao.module.pay.controller.admin.order.vo; | 1 | package cn.iocoder.yudao.module.pay.controller.admin.order.vo; |
| 2 | 2 | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | -import lombok.AllArgsConstructor; | ||
| 5 | -import lombok.Builder; | ||
| 6 | import lombok.Data; | 4 | import lombok.Data; |
| 7 | -import lombok.NoArgsConstructor; | ||
| 8 | -import lombok.experimental.Accessors; | ||
| 9 | 5 | ||
| 10 | @Schema(description = "管理后台 - 支付订单提交 Response VO") | 6 | @Schema(description = "管理后台 - 支付订单提交 Response VO") |
| 11 | @Data | 7 | @Data |
| 12 | public class PayOrderSubmitRespVO { | 8 | public class PayOrderSubmitRespVO { |
| 13 | 9 | ||
| 10 | + @Schema(description = "支付状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") // 参见 PayOrderStatusEnum 枚举 | ||
| 11 | + private Integer status; | ||
| 12 | + | ||
| 14 | @Schema(description = "展示模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "url") // 参见 PayDisplayModeEnum 枚举 | 13 | @Schema(description = "展示模式", requiredMode = Schema.RequiredMode.REQUIRED, example = "url") // 参见 PayDisplayModeEnum 枚举 |
| 15 | private String displayMode; | 14 | private String displayMode; |
| 16 | - | ||
| 17 | @Schema(description = "展示内容", requiredMode = Schema.RequiredMode.REQUIRED) | 15 | @Schema(description = "展示内容", requiredMode = Schema.RequiredMode.REQUIRED) |
| 18 | private String displayContent; | 16 | private String displayContent; |
| 19 | 17 |
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}} | @@ -19,7 +19,7 @@ tenant-id: {{appTenentId}} | ||
| 19 | "id": 202, | 19 | "id": 202, |
| 20 | "channelCode": "wx_bar", | 20 | "channelCode": "wx_bar", |
| 21 | "channelExtras": { | 21 | "channelExtras": { |
| 22 | - "authCode": "132990241553789274" | 22 | + "authCode": "134042110834344848" |
| 23 | } | 23 | } |
| 24 | } | 24 | } |
| 25 | 25 |
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 { | @@ -93,7 +93,7 @@ public interface PayOrderConvert { | ||
| 93 | 93 | ||
| 94 | PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqVO reqVO, String userIp); | 94 | PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqVO reqVO, String userIp); |
| 95 | 95 | ||
| 96 | - PayOrderSubmitRespVO convert(PayOrderUnifiedRespDTO bean); | 96 | + PayOrderSubmitRespVO convert(PayOrderDO order, PayOrderUnifiedRespDTO unifiedRespDTO); |
| 97 | 97 | ||
| 98 | AppPayOrderSubmitRespVO convert3(PayOrderSubmitRespVO bean); | 98 | AppPayOrderSubmitRespVO convert3(PayOrderSubmitRespVO bean); |
| 99 | 99 |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
| 1 | package cn.iocoder.yudao.module.pay.service.order; | 1 | package cn.iocoder.yudao.module.pay.service.order; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.date.DateUtil; | 3 | import cn.hutool.core.date.DateUtil; |
| 4 | +import cn.hutool.core.lang.Pair; | ||
| 4 | import cn.hutool.core.util.ObjectUtil; | 5 | import cn.hutool.core.util.ObjectUtil; |
| 5 | import cn.hutool.core.util.RandomUtil; | 6 | import cn.hutool.core.util.RandomUtil; |
| 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 7 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 8 | +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; | ||
| 7 | import cn.iocoder.yudao.framework.pay.config.PayProperties; | 9 | import cn.iocoder.yudao.framework.pay.config.PayProperties; |
| 8 | import cn.iocoder.yudao.framework.pay.core.client.PayClient; | 10 | import cn.iocoder.yudao.framework.pay.core.client.PayClient; |
| 9 | import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; | 11 | import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; |
| @@ -28,6 +30,7 @@ import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; | @@ -28,6 +30,7 @@ import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; | ||
| 28 | import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; | 30 | import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; |
| 29 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; | 31 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderNotifyStatusEnum; |
| 30 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; | 32 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
| 33 | +import cn.iocoder.yudao.module.pay.enums.refund.PayRefundTypeEnum; | ||
| 31 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; | 34 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; |
| 32 | import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; | 35 | import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; |
| 33 | import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; | 36 | import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; |
| @@ -41,6 +44,7 @@ import javax.annotation.Resource; | @@ -41,6 +44,7 @@ import javax.annotation.Resource; | ||
| 41 | import java.time.LocalDateTime; | 44 | import java.time.LocalDateTime; |
| 42 | import java.util.Collection; | 45 | import java.util.Collection; |
| 43 | import java.util.List; | 46 | import java.util.List; |
| 47 | +import java.util.Objects; | ||
| 44 | 48 | ||
| 45 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 49 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 46 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | 50 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; |
| @@ -109,22 +113,19 @@ public class PayOrderServiceImpl implements PayOrderService { | @@ -109,22 +113,19 @@ public class PayOrderServiceImpl implements PayOrderService { | ||
| 109 | } | 113 | } |
| 110 | 114 | ||
| 111 | // 创建支付交易单 | 115 | // 创建支付交易单 |
| 112 | - order = PayOrderConvert.INSTANCE.convert(reqDTO).setAppId(app.getId()); | ||
| 113 | - // 商户相关字段 | ||
| 114 | - order.setNotifyUrl(app.getPayNotifyUrl()) | ||
| 115 | - .setNotifyStatus(PayOrderNotifyStatusEnum.NO.getStatus()); | ||
| 116 | - // 订单相关字段 | ||
| 117 | - order.setStatus(PayOrderStatusEnum.WAITING.getStatus()); | ||
| 118 | - // 退款相关字段 | ||
| 119 | - // todo @芋艿 创建支付的订单的退款状态枚举是不是有问题,应该是 PayRefundTypeEnum 吧 您这填写的是 PayOrderNotifyStatusEnum 回调状态枚举 | ||
| 120 | - order.setRefundStatus(PayOrderNotifyStatusEnum.NO.getStatus()) | ||
| 121 | - .setRefundTimes(0).setRefundPrice(0L); | 116 | + order = PayOrderConvert.INSTANCE.convert(reqDTO).setAppId(app.getId()) |
| 117 | + // 商户相关字段 | ||
| 118 | + .setNotifyUrl(app.getPayNotifyUrl()).setNotifyStatus(PayOrderNotifyStatusEnum.NO.getStatus()) | ||
| 119 | + // 订单相关字段 | ||
| 120 | + .setStatus(PayOrderStatusEnum.WAITING.getStatus()) | ||
| 121 | + // 退款相关字段 | ||
| 122 | + .setRefundStatus(PayRefundTypeEnum.NO.getStatus()).setRefundTimes(0).setRefundPrice(0L); | ||
| 122 | orderMapper.insert(order); | 123 | orderMapper.insert(order); |
| 123 | - // 最终返回 | ||
| 124 | return order.getId(); | 124 | return order.getId(); |
| 125 | } | 125 | } |
| 126 | 126 | ||
| 127 | @Override | 127 | @Override |
| 128 | + @Transactional(rollbackFor = Exception.class) | ||
| 128 | public PayOrderSubmitRespVO submitPayOrder(PayOrderSubmitReqVO reqVO, String userIp) { | 129 | public PayOrderSubmitRespVO submitPayOrder(PayOrderSubmitReqVO reqVO, String userIp) { |
| 129 | // 1. 获得 PayOrderDO ,并校验其是否存在 | 130 | // 1. 获得 PayOrderDO ,并校验其是否存在 |
| 130 | PayOrderDO order = validatePayOrderCanSubmit(reqVO.getId()); | 131 | PayOrderDO order = validatePayOrderCanSubmit(reqVO.getId()); |
| @@ -150,9 +151,15 @@ public class PayOrderServiceImpl implements PayOrderService { | @@ -150,9 +151,15 @@ public class PayOrderServiceImpl implements PayOrderService { | ||
| 150 | .setAmount(order.getPrice()).setExpireTime(order.getExpireTime()); | 151 | .setAmount(order.getPrice()).setExpireTime(order.getExpireTime()); |
| 151 | PayOrderUnifiedRespDTO unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO); | 152 | PayOrderUnifiedRespDTO unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO); |
| 152 | 153 | ||
| 153 | - // TODO 轮询三方接口,是否已经支付的任务 | 154 | + // 4. 如果调用直接支付成功,则直接更新支付单状态为成功。例如说:付款码支付,免密支付时,就直接验证支付成功 |
| 155 | + if (unifiedOrderRespDTO.getNotify() != null) { | ||
| 156 | + notifyPayOrderSuccess(channel, unifiedOrderRespDTO.getNotify(), null); | ||
| 157 | + // 此处需要读取最新的状态 | ||
| 158 | + order = orderMapper.selectById(order.getId()); | ||
| 159 | + } | ||
| 160 | + | ||
| 154 | // 返回成功 | 161 | // 返回成功 |
| 155 | - return PayOrderConvert.INSTANCE.convert(unifiedOrderRespDTO); | 162 | + return PayOrderConvert.INSTANCE.convert(order, unifiedOrderRespDTO); |
| 156 | } | 163 | } |
| 157 | 164 | ||
| 158 | private PayOrderDO validatePayOrderCanSubmit(Long id) { | 165 | private PayOrderDO validatePayOrderCanSubmit(Long id) { |
| @@ -163,6 +170,9 @@ public class PayOrderServiceImpl implements PayOrderService { | @@ -163,6 +170,9 @@ public class PayOrderServiceImpl implements PayOrderService { | ||
| 163 | if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 | 170 | if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 |
| 164 | throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); | 171 | throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); |
| 165 | } | 172 | } |
| 173 | + if (LocalDateTimeUtils.beforeNow(order.getExpireTime())) { // 校验是否过期 | ||
| 174 | + throw exception(ErrorCodeConstants.PAY_ORDER_IS_EXPIRED); | ||
| 175 | + } | ||
| 166 | return order; | 176 | return order; |
| 167 | } | 177 | } |
| 168 | 178 | ||
| @@ -214,17 +224,22 @@ public class PayOrderServiceImpl implements PayOrderService { | @@ -214,17 +224,22 @@ public class PayOrderServiceImpl implements PayOrderService { | ||
| 214 | public void notifyPayOrder(Long channelId, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { | 224 | public void notifyPayOrder(Long channelId, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { |
| 215 | // 校验支付渠道是否有效 | 225 | // 校验支付渠道是否有效 |
| 216 | PayChannelDO channel = channelService.validPayChannel(channelId); | 226 | PayChannelDO channel = channelService.validPayChannel(channelId); |
| 217 | - TenantUtils.execute(channel.getTenantId(), () -> { | ||
| 218 | - // 1. 更新 PayOrderExtensionDO 支付成功 | ||
| 219 | - PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify.getOrderExtensionNo(), | ||
| 220 | - rawNotify); | ||
| 221 | - // 2. 更新 PayOrderDO 支付成功 | ||
| 222 | - PayOrderDO order = updatePayOrderSuccess(channel, orderExtension, notify); | ||
| 223 | - | ||
| 224 | - // 3. 插入支付通知记录 | ||
| 225 | - notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() | ||
| 226 | - .type(PayNotifyTypeEnum.ORDER.getType()).dataId(order.getId()).build()); | ||
| 227 | - }); | 227 | + // 更新支付订单为已支付 |
| 228 | + TenantUtils.execute(channel.getTenantId(), () -> notifyPayOrderSuccess(channel, notify, rawNotify)); | ||
| 229 | + } | ||
| 230 | + | ||
| 231 | + private void notifyPayOrderSuccess(PayChannelDO channel, PayOrderNotifyRespDTO notify, PayNotifyReqDTO rawNotify) { | ||
| 232 | + // 1. 更新 PayOrderExtensionDO 支付成功 | ||
| 233 | + PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notify.getOrderExtensionNo(), rawNotify); | ||
| 234 | + // 2. 更新 PayOrderDO 支付成功 | ||
| 235 | + Pair<Boolean, PayOrderDO> order = updatePayOrderSuccess(channel, orderExtension, notify); | ||
| 236 | + if (order.getKey()) { // 如果之前已经成功回调,则直接返回,不用重复记录支付通知记录;例如说:支付平台重复回调 | ||
| 237 | + return; | ||
| 238 | + } | ||
| 239 | + | ||
| 240 | + // 3. 插入支付通知记录 | ||
| 241 | + notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() | ||
| 242 | + .type(PayNotifyTypeEnum.ORDER.getType()).dataId(order.getValue().getId()).build()); | ||
| 228 | } | 243 | } |
| 229 | 244 | ||
| 230 | /** | 245 | /** |
| @@ -235,15 +250,20 @@ public class PayOrderServiceImpl implements PayOrderService { | @@ -235,15 +250,20 @@ public class PayOrderServiceImpl implements PayOrderService { | ||
| 235 | * @return PayOrderExtensionDO 对象 | 250 | * @return PayOrderExtensionDO 对象 |
| 236 | */ | 251 | */ |
| 237 | private PayOrderExtensionDO updatePayOrderExtensionSuccess(String no, PayNotifyReqDTO rawNotify) { | 252 | private PayOrderExtensionDO updatePayOrderExtensionSuccess(String no, PayNotifyReqDTO rawNotify) { |
| 238 | - // 1.1 查询 PayOrderExtensionDO | 253 | + // 1. 查询 PayOrderExtensionDO |
| 239 | PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(no); | 254 | PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(no); |
| 240 | if (orderExtension == null) { | 255 | if (orderExtension == null) { |
| 241 | throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND); | 256 | throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND); |
| 242 | } | 257 | } |
| 258 | + if (PayOrderStatusEnum.isSuccess(orderExtension.getStatus())) { // 如果已经是成功,直接返回,不用重复更新 | ||
| 259 | + log.info("[updatePayOrderSuccess][支付拓展单({}) 已经是已支付,无需更新为已支付]", orderExtension.getId()); | ||
| 260 | + return orderExtension; | ||
| 261 | + } | ||
| 243 | if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付 | 262 | if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付 |
| 244 | throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); | 263 | throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING); |
| 245 | } | 264 | } |
| 246 | - // 1.2 更新 PayOrderExtensionDO | 265 | + |
| 266 | + // 2. 更新 PayOrderExtensionDO | ||
| 247 | int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), | 267 | int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), |
| 248 | PayOrderStatusEnum.WAITING.getStatus(), PayOrderExtensionDO.builder().id(orderExtension.getId()) | 268 | PayOrderStatusEnum.WAITING.getStatus(), PayOrderExtensionDO.builder().id(orderExtension.getId()) |
| 249 | .status(PayOrderStatusEnum.SUCCESS.getStatus()) | 269 | .status(PayOrderStatusEnum.SUCCESS.getStatus()) |
| @@ -261,19 +281,26 @@ public class PayOrderServiceImpl implements PayOrderService { | @@ -261,19 +281,26 @@ public class PayOrderServiceImpl implements PayOrderService { | ||
| 261 | * @param channel 支付渠道 | 281 | * @param channel 支付渠道 |
| 262 | * @param orderExtension 支付拓展单 | 282 | * @param orderExtension 支付拓展单 |
| 263 | * @param notify 通知回调 | 283 | * @param notify 通知回调 |
| 264 | - * @return PayOrderDO 对象 | 284 | + * @return key:是否之前已经成功回调 |
| 285 | + * value:PayOrderDO 对象 | ||
| 265 | */ | 286 | */ |
| 266 | - private PayOrderDO updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, | ||
| 267 | - PayOrderNotifyRespDTO notify) { | ||
| 268 | - // 2.1 判断 PayOrderDO 是否处于待支付 | 287 | + private Pair<Boolean, PayOrderDO> updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension, |
| 288 | + PayOrderNotifyRespDTO notify) { | ||
| 289 | + // 1. 判断 PayOrderDO 是否处于待支付 | ||
| 269 | PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId()); | 290 | PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId()); |
| 270 | if (order == null) { | 291 | if (order == null) { |
| 271 | throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND); | 292 | throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND); |
| 272 | } | 293 | } |
| 294 | + if (PayOrderStatusEnum.isSuccess(order.getStatus()) // 如果已经是成功,直接返回,不用重复更新 | ||
| 295 | + && Objects.equals(order.getSuccessExtensionId(), orderExtension.getId())) { | ||
| 296 | + log.info("[updatePayOrderSuccess][支付订单({}) 已经是已支付,无需更新为已支付]", order.getId()); | ||
| 297 | + return Pair.of(true, order); | ||
| 298 | + } | ||
| 273 | if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 | 299 | if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 |
| 274 | throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); | 300 | throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); |
| 275 | } | 301 | } |
| 276 | - // 2.2 更新 PayOrderDO | 302 | + |
| 303 | + // 2. 更新 PayOrderDO | ||
| 277 | int updateCounts = orderMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(), | 304 | int updateCounts = orderMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(), |
| 278 | PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()) | 305 | PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()) |
| 279 | .channelId(channel.getId()).channelCode(channel.getCode()) | 306 | .channelId(channel.getId()).channelCode(channel.getCode()) |
| @@ -284,7 +311,7 @@ public class PayOrderServiceImpl implements PayOrderService { | @@ -284,7 +311,7 @@ public class PayOrderServiceImpl implements PayOrderService { | ||
| 284 | throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); | 311 | throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING); |
| 285 | } | 312 | } |
| 286 | log.info("[updatePayOrderSuccess][支付订单({}) 更新为已支付]", order.getId()); | 313 | log.info("[updatePayOrderSuccess][支付订单({}) 更新为已支付]", order.getId()); |
| 287 | - return order; | 314 | + return Pair.of(false, order); |
| 288 | } | 315 | } |
| 289 | 316 | ||
| 290 | } | 317 | } |
yudao-ui-admin/src/views/pay/cashier/index.vue
| @@ -27,7 +27,7 @@ | @@ -27,7 +27,7 @@ | ||
| 27 | <el-descriptions title="选择微信支付" style="margin-top: 20px;" /> | 27 | <el-descriptions title="选择微信支付" style="margin-top: 20px;" /> |
| 28 | <div class="pay-channel-container"> | 28 | <div class="pay-channel-container"> |
| 29 | <div class="box" v-for="channel in channels" v-if="channel.code.indexOf('wx_') === 0" :key="channel.code"> | 29 | <div class="box" v-for="channel in channels" v-if="channel.code.indexOf('wx_') === 0" :key="channel.code"> |
| 30 | - <img :src="icons[channel.code]"> | 30 | + <img :src="channel.icon"> |
| 31 | <div class="title">{{ channel.name }}</div> | 31 | <div class="title">{{ channel.name }}</div> |
| 32 | </div> | 32 | </div> |
| 33 | </div> | 33 | </div> |
| @@ -36,7 +36,7 @@ | @@ -36,7 +36,7 @@ | ||
| 36 | <div class="pay-channel-container"> | 36 | <div class="pay-channel-container"> |
| 37 | <div class="box" v-for="channel in channels" :key="channel.code" | 37 | <div class="box" v-for="channel in channels" :key="channel.code" |
| 38 | v-if="channel.code.indexOf('alipay_') === -1 && channel.code.indexOf('wx_') === -1"> | 38 | v-if="channel.code.indexOf('alipay_') === -1 && channel.code.indexOf('wx_') === -1"> |
| 39 | - <img :src="icons[channel.code]"> | 39 | + <img :src="channel.icon"> |
| 40 | <div class="title">{{ channel.name }}</div> | 40 | <div class="title">{{ channel.name }}</div> |
| 41 | </div> | 41 | </div> |
| 42 | </div> | 42 | </div> |
| @@ -132,7 +132,7 @@ export default { | @@ -132,7 +132,7 @@ export default { | ||
| 132 | code: "wx_lite" | 132 | code: "wx_lite" |
| 133 | }, { | 133 | }, { |
| 134 | name: '微信 App 支付', | 134 | name: '微信 App 支付', |
| 135 | - icon: require("@/assets/images/pay/icon/wx_lite.svg"), | 135 | + icon: require("@/assets/images/pay/icon/wx_app.svg"), |
| 136 | code: "wx_app" | 136 | code: "wx_app" |
| 137 | }, { | 137 | }, { |
| 138 | name: '模拟支付', | 138 | name: '模拟支付', |