Commit cad508def69c402efe0b6d7179eeabd6d572d630

Authored by YunaiV
1 parent ca4afc29

mall + pay:

1. bar 扫码支付成功后,额外返回 notify
2. notify 在支付回调时,增加幂等处理
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: '模拟支付',