Commit 8798944069a499c098356ca3c8d0c1a5b49a5096

Authored by YunaiV
1 parent 73d847ae

优化 pay 支付成功后的回调逻辑

Showing 18 changed files with 132 additions and 141 deletions
http-client.env.json
... ... @@ -5,7 +5,7 @@
5 5 "adminTenentId": "1",
6 6  
7 7 "appApi": "http://127.0.0.1:48080/app-api",
8   - "appToken": "test1",
  8 + "appToken": "test247",
9 9 "appTenentId": "1"
10 10 },
11 11 "gateway": {
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/PayProperties.java
... ... @@ -13,26 +13,23 @@ import javax.validation.constraints.NotEmpty;
13 13 public class PayProperties {
14 14  
15 15 /**
16   - * 支付回调地址
  16 + * 回调地址
  17 + *
  18 + * 实际上,对应的 PayNotifyController 的 notifyCallback 方法的 URL
  19 + *
17 20 * 注意,支付渠道统一回调到 payNotifyUrl 地址,由支付模块统一处理;然后,自己的支付模块,在回调 PayAppDO.payNotifyUrl 地址
18 21 */
19   - @NotEmpty(message = "支付回调地址不能为空")
20   - @URL(message = "支付回调地址的格式必须是 URL")
21   - private String payNotifyUrl;
22   - /**
23   - * 退款回调地址
24   - * 注意点,同 {@link #payNotifyUrl} 属性
25   - */
26   - @NotEmpty(message = "退款回调地址不能为空")
27   - @URL(message = "退款回调地址的格式必须是 URL")
28   - private String refundNotifyUrl;
29   -
  22 + @NotEmpty(message = "回调地址不能为空")
  23 + @URL(message = "回调地址的格式必须是 URL")
  24 + private String callbackUrl;
30 25  
31 26 /**
32   - * 支付完成的返回地址
  27 + * 回跳地址
  28 + *
  29 + * 实际上,对应的 PayNotifyController 的 returnCallback 方法的 URL
33 30 */
34   - @URL(message = "支付返回的地址的格式必须是 URL")
35   - @NotEmpty(message = "支付返回的地址不能为空")
36   - private String payReturnUrl;
  31 + @URL(message = "回跳地址的格式必须是 URL")
  32 + @NotEmpty(message = "回跳地址不能为空")
  33 + private String returnUrl;
37 34  
38 35 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayNotifyDataDTO.java
... ... @@ -21,9 +21,9 @@ public class PayNotifyDataDTO {
21 21 */
22 22 private String body;
23 23  
24   -
25 24 /**
26 25 * HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数
27 26 */
28 27 private Map<String,String> params;
  28 +
29 29 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http
... ... @@ -8,18 +8,20 @@ GET {{shop-api-base-url}}/trade-order/confirm-create-order-info-from-cart
8 8 Content-Type: application/x-www-form-urlencoded
9 9 Authorization: Bearer {{user-access-token}}
10 10  
11   -### /trade-order/confirm-create-order-info-from-cart 基于商品,创建订单
12   -POST {{shop-api-base-url}}/trade-order/create
  11 +### /trade-order/create 基于商品,创建订单
  12 +POST {{appApi}}/trade/order/create
13 13 Content-Type: application/json
14   -Authorization: Bearer {{user-access-token}}
  14 +Authorization: Bearer {{appToken}}
  15 +tenant-id: {{appTenentId}}
15 16  
16 17 {
17   - "userAddressId": 19,
  18 + "addressId": 21,
18 19 "remark": "我是备注",
19   - "orderItems": [
  20 + "fromCart": false,
  21 + "items": [
20 22 {
21   - "skuId": 3,
22   - "quantity": 1
  23 + "skuId": 29,
  24 + "count": 1
23 25 }
24 26 ]
25 27 }
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java
... ... @@ -4,6 +4,7 @@ import io.swagger.annotations.ApiModel;
4 4 import io.swagger.annotations.ApiModelProperty;
5 5 import lombok.Data;
6 6  
  7 +import javax.validation.Valid;
7 8 import javax.validation.constraints.Min;
8 9 import javax.validation.constraints.NotEmpty;
9 10 import javax.validation.constraints.NotNull;
... ... @@ -31,6 +32,7 @@ public class AppTradeOrderCreateReqVO {
31 32 * 订单商品项列表
32 33 */
33 34 @NotEmpty(message = "必须选择购买的商品")
  35 + @Valid
34 36 private List<Item> items;
35 37  
36 38 @ApiModel(value = "订单商品项")
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
... ... @@ -30,6 +30,7 @@ public interface TradeOrderConvert {
30 30 TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class);
31 31  
32 32 @Mappings({
  33 + @Mapping(target = "id", ignore = true),
33 34 @Mapping(source = "createReqVO.couponId", target = "couponId"),
34 35 @Mapping(target = "remark", ignore = true),
35 36 @Mapping(source = "createReqVO.remark", target = "userRemark"),
... ...
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderServiceImpl.java
... ... @@ -167,7 +167,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
167 167 * @return 收件地址
168 168 */
169 169 private AddressRespDTO validateAddress(Long userId, Long addressId) {
170   - AddressRespDTO address = addressApi.getAddress(userId, addressId);
  170 + AddressRespDTO address = addressApi.getAddress(addressId, userId);
171 171 if (Objects.isNull(address)) {
172 172 throw exception(ErrorCodeConstants.ORDER_CREATE_ADDRESS_NOT_FOUND);
173 173 }
... ...
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/order/dto/PayOrderCreateReqDTO.java
... ... @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.pay.api.order.dto;
3 3 import lombok.Data;
4 4 import org.hibernate.validator.constraints.Length;
5 5  
6   -import javax.validation.constraints.Min;
  6 +import javax.validation.constraints.DecimalMin;
7 7 import javax.validation.constraints.NotEmpty;
8 8 import javax.validation.constraints.NotNull;
9 9 import java.io.Serializable;
... ... @@ -11,8 +11,6 @@ import java.time.LocalDateTime;
11 11  
12 12 /**
13 13 * 支付单创建 Request DTO
14   - *
15   - * @author LeeYan9
16 14 */
17 15 @Data
18 16 public class PayOrderCreateReqDTO implements Serializable {
... ... @@ -44,7 +42,7 @@ public class PayOrderCreateReqDTO implements Serializable {
44 42 /**
45 43 * 商品描述
46 44 */
47   -// @NotEmpty(message = "商品描述信息不能为空") // 允许空
  45 +// @NotEmpty(message = "商品描述信息不能为空")
48 46 @Length(max = 128, message = "商品描述信息长度不能超过128")
49 47 private String body;
50 48  
... ... @@ -54,7 +52,7 @@ public class PayOrderCreateReqDTO implements Serializable {
54 52 * 支付金额,单位:分
55 53 */
56 54 @NotNull(message = "支付金额不能为空")
57   - @Min(value = 1, message = "支付金额必须大于零")
  55 + @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
58 56 private Integer amount;
59 57  
60 58 /**
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/api/order/PayOrderApiImpl.java
1 1 package cn.iocoder.yudao.module.pay.api.order;
2 2  
3 3 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
  4 +import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
4 5 import org.springframework.stereotype.Service;
5 6  
  7 +import javax.annotation.Resource;
  8 +
6 9 /**
7   - * TODO 注释
  10 + * 支付单 API 实现类
  11 + *
  12 + * @author 芋道源码
8 13 */
9 14 @Service
10 15 public class PayOrderApiImpl implements PayOrderApi {
11 16  
  17 + @Resource
  18 + private PayOrderService payOrderService;
  19 +
12 20 @Override
13 21 public Long createPayOrder(PayOrderCreateReqDTO reqDTO) {
14   - return null;
  22 + return payOrderService.createPayOrder(reqDTO);
15 23 }
16 24  
17 25 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
1 1 package cn.iocoder.yudao.module.pay.controller.admin.notify;
2 2  
  3 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
3 4 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
4 5 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
5 6 import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
... ... @@ -61,6 +62,7 @@ public class PayNotifyController {
61 62 @PostMapping(value = "/callback/{channelId}")
62 63 @ApiOperation(value = "支付渠道的统一回调接口", notes = "包括支付回调,退款回调")
63 64 @PermitAll
  65 + @OperateLog(enable = false) // 回调地址,无需记录操作日志
64 66 public String notifyCallback(@PathVariable("channelId") Long channelId,
65 67 @RequestParam Map<String, String> params,
66 68 @RequestBody String body) throws Exception {
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http 0 → 100644
  1 +### /pay/create 提交支付订单
  2 +POST {{appApi}}/pay/order/submit
  3 +Content-Type: application/json
  4 +Authorization: Bearer {{appToken}}
  5 +tenant-id: {{appTenentId}}
  6 +
  7 +{
  8 + "id": 125,
  9 + "channelCode": "alipay_qr"
  10 +}
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java
1 1 package cn.iocoder.yudao.module.pay.convert.order;
2 2  
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 4 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
  5 +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
4 6 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderDetailsRespVO;
5 7 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExcelVO;
6 8 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageItemRespVO;
7 9 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderRespVO;
8   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 10 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
10 11 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
11   -import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderCreateReqDTO;
12 12 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO;
13 13 import org.mapstruct.Mapper;
  14 +import org.mapstruct.Mapping;
14 15 import org.mapstruct.factory.Mappers;
15 16  
16 17 import java.math.BigDecimal;
... ... @@ -88,6 +89,7 @@ public interface PayOrderConvert {
88 89  
89 90 PayOrderDO convert(PayOrderCreateReqDTO bean);
90 91  
  92 + @Mapping(target = "id", ignore = true)
91 93 PayOrderExtensionDO convert(PayOrderSubmitReqDTO bean);
92 94  
93 95 PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqDTO bean);
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderService.java
... ... @@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
6 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 8 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
9   -import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderCreateReqDTO;
  9 +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
10 10 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO;
11 11 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitRespDTO;
12 12  
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
1 1 package cn.iocoder.yudao.module.pay.service.order;
2 2  
3 3 import cn.hutool.core.date.DateUtil;
  4 +import cn.hutool.core.util.ObjectUtil;
4 5 import cn.hutool.core.util.RandomUtil;
5   -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
6 6 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 8 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
8 9 import cn.iocoder.yudao.framework.pay.config.PayProperties;
9 10 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
... ... @@ -11,6 +12,8 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
11 12 import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
12 13 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO;
13 14 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
  15 +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
  16 +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
14 17 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO;
15 18 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
16 19 import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert;
... ... @@ -28,8 +31,6 @@ import cn.iocoder.yudao.module.pay.service.merchant.PayAppService;
28 31 import cn.iocoder.yudao.module.pay.service.merchant.PayChannelService;
29 32 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
30 33 import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;
31   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
32   -import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderCreateReqDTO;
33 34 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO;
34 35 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitRespDTO;
35 36 import lombok.extern.slf4j.Slf4j;
... ... @@ -43,6 +44,8 @@ import java.util.Collection;
43 44 import java.util.List;
44 45 import java.util.Objects;
45 46  
  47 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  48 +
46 49 /**
47 50 * 支付订单 Service 实现类
48 51 *
... ... @@ -133,16 +136,16 @@ public class PayOrderServiceImpl implements PayOrderService {
133 136 PayClient client = payClientFactory.getPayClient(channel.getId());
134 137 if (client == null) {
135 138 log.error("[submitPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
136   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
  139 + throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
137 140 }
138 141  
139 142 // 获得 PayOrderDO ,并校验其是否存在
140 143 PayOrderDO order = orderMapper.selectById(reqDTO.getId());
141 144 if (order == null || !Objects.equals(order.getAppId(), reqDTO.getAppId())) { // 是否存在
142   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
  145 + throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
143 146 }
144 147 if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
145   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
  148 + throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
146 149 }
147 150  
148 151 // 插入 PayOrderExtensionDO
... ... @@ -177,7 +180,7 @@ public class PayOrderServiceImpl implements PayOrderService {
177 180 * @return 支付成功返回的地址。 配置地址 + "/" + channel id
178 181 */
179 182 private String genChannelReturnUrl(PayChannelDO channel) {
180   - return payProperties.getPayReturnUrl() + "/" + channel.getId();
  183 + return payProperties.getReturnUrl() + "/" + channel.getId();
181 184 }
182 185  
183 186 /**
... ... @@ -187,8 +190,7 @@ public class PayOrderServiceImpl implements PayOrderService {
187 190 * @return 支付渠道的回调地址 配置地址 + "/" + channel id
188 191 */
189 192 private String genChannelPayNotifyUrl(PayChannelDO channel) {
190   - //去掉channel code, 似乎没啥用, 用统一的回调地址
191   - return payProperties.getPayNotifyUrl() + "/" + channel.getId();
  193 + return payProperties.getCallbackUrl() + "/" + channel.getId();
192 194 }
193 195  
194 196 private String generateOrderExtensionNo() {
... ... @@ -210,64 +212,99 @@ public class PayOrderServiceImpl implements PayOrderService {
210 212 }
211 213  
212 214 @Override
213   - @Transactional
214   - public void notifyPayOrder(Long channelId, PayNotifyDataDTO notifyData) throws Exception {
  215 + @Transactional(rollbackFor = Exception.class)
  216 + public void notifyPayOrder(Long channelId, PayNotifyDataDTO notifyData) {
215 217 // TODO 芋艿,记录回调日志
216 218 log.info("[notifyPayOrder][channelId({}) 回调数据({})]", channelId, notifyData.getBody());
217 219  
218 220 // 校验支付渠道是否有效
219 221 PayChannelDO channel = channelService.validPayChannel(channelId);
  222 + TenantUtils.execute(channel.getTenantId(), () -> {
  223 + try {
  224 + notifyPayOrder(channel, notifyData);
  225 + } catch (Exception e) {
  226 + throw new RuntimeException(e);
  227 + }
  228 + });
  229 + }
  230 +
  231 + private void notifyPayOrder(PayChannelDO channel, PayNotifyDataDTO notifyData) throws Exception {
220 232 // 校验支付客户端是否正确初始化
221 233 PayClient client = payClientFactory.getPayClient(channel.getId());
222 234 if (client == null) {
223 235 log.error("[notifyPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
224   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
  236 + throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
225 237 }
226 238  
227   - // 解析支付结果
  239 + // 0. 解析支付结果
228 240 PayOrderNotifyRespDTO notifyRespDTO = client.parseOrderNotify(notifyData);
  241 + // 1. 更新 PayOrderExtensionDO 支付成功
  242 + PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notifyRespDTO.getOrderExtensionNo(), notifyData.getBody());
  243 + // 2. 更新 PayOrderDO 支付成功
  244 + PayOrderDO order = updatePayOrderSuccess(channel, orderExtension, notifyRespDTO);
229 245  
230   - // TODO 芋艿,先最严格的校验。即使调用方重复调用,实际哪个订单已经被重复回调的支付,也返回 false 。也没问题,因为实际已经回调成功了。
  246 + // 3. 插入支付通知记录
  247 + notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()
  248 + .type(PayNotifyTypeEnum.ORDER.getType()).dataId(order.getId()).build());
  249 + }
  250 +
  251 + /**
  252 + * 更新 PayOrderExtensionDO 支付成功
  253 + *
  254 + * @param no 支付订单号(支付模块)
  255 + * @param body 回调内容
  256 + * @return PayOrderExtensionDO 对象
  257 + */
  258 + private PayOrderExtensionDO updatePayOrderExtensionSuccess(String no, String body) {
231 259 // 1.1 查询 PayOrderExtensionDO
232   - PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notifyRespDTO.getOrderExtensionNo());
  260 + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(no);
233 261 if (orderExtension == null) {
234   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND);
  262 + throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND);
235 263 }
236   - if (!PayOrderStatusEnum.WAITING.getStatus().equals(orderExtension.getStatus())) { // 校验状态,必须是待支付
237   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
  264 + if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付
  265 + throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
238 266 }
239 267 // 1.2 更新 PayOrderExtensionDO
240   - //TODO 支付宝交易超时 TRADE_FINISHED 需要更新交易关闭
241 268 int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(),
242 269 PayOrderStatusEnum.WAITING.getStatus(), PayOrderExtensionDO.builder().id(orderExtension.getId())
243   - .status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(notifyData.getBody()).build());
  270 + .status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(body).build());
244 271 if (updateCounts == 0) { // 校验状态,必须是待支付
245   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
  272 + throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
246 273 }
247   - log.info("[notifyPayOrder][支付拓展单({}) 更新为已支付]", orderExtension.getId());
  274 + log.info("[updatePayOrderSuccess][支付拓展单({}) 更新为已支付]", orderExtension.getId());
  275 + return orderExtension;
  276 + }
248 277  
  278 + /**
  279 + * 更新 PayOrderDO 支付成功
  280 + *
  281 + * @param channel 支付渠道
  282 + * @param orderExtension 支付拓展单
  283 + * @param notifyRespDTO 通知回调
  284 + * @return PayOrderDO 对象
  285 + */
  286 + private PayOrderDO updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
  287 + PayOrderNotifyRespDTO notifyRespDTO) {
249 288 // 2.1 判断 PayOrderDO 是否处于待支付
250 289 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
251 290 if (order == null) {
252   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
  291 + throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
253 292 }
254 293 if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
255   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
  294 + throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
256 295 }
257 296 // 2.2 更新 PayOrderDO
258   - updateCounts = orderMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(),
259   - PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelId(channelId).channelCode(channel.getCode())
  297 + int updateCounts = orderMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(),
  298 + PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus())
  299 + .channelId(channel.getId()).channelCode(channel.getCode())
260 300 .successTime(notifyRespDTO.getSuccessTime()).successExtensionId(orderExtension.getId())
261 301 .channelOrderNo(notifyRespDTO.getChannelOrderNo()).channelUserId(notifyRespDTO.getChannelUserId())
262 302 .notifyTime(LocalDateTime.now()).build());
263 303 if (updateCounts == 0) { // 校验状态,必须是待支付
264   - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
  304 + throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
265 305 }
266   - log.info("[notifyPayOrder][支付订单({}) 更新为已支付]", order.getId());
267   -
268   - // 3. 插入支付通知记录
269   - notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()
270   - .type(PayNotifyTypeEnum.ORDER.getType()).dataId(order.getId()).build());
  306 + log.info("[updatePayOrderSuccess][支付订单({}) 更新为已支付]", order.getId());
  307 + return order;
271 308 }
272 309  
273 310 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/dto/PayOrderCreateReqDTO.java deleted
1   -package cn.iocoder.yudao.module.pay.service.order.dto;
2   -
3   -import lombok.Data;
4   -import org.hibernate.validator.constraints.Length;
5   -
6   -import javax.validation.constraints.DecimalMin;
7   -import javax.validation.constraints.NotEmpty;
8   -import javax.validation.constraints.NotNull;
9   -import java.io.Serializable;
10   -import java.time.LocalDateTime;
11   -
12   -/**
13   - * 支付单创建 Request DTO
14   - */
15   -@Data
16   -public class PayOrderCreateReqDTO implements Serializable {
17   -
18   - /**
19   - * 应用编号
20   - */
21   - @NotNull(message = "应用编号不能为空")
22   - private Long appId;
23   - /**
24   - * 用户 IP
25   - */
26   - @NotEmpty(message = "用户 IP 不能为空")
27   - private String userIp;
28   -
29   - // ========== 商户相关字段 ==========
30   -
31   - /**
32   - * 商户订单编号
33   - */
34   - @NotEmpty(message = "商户订单编号不能为空")
35   - private String merchantOrderId;
36   - /**
37   - * 商品标题
38   - */
39   - @NotEmpty(message = "商品标题不能为空")
40   - @Length(max = 32, message = "商品标题不能超过 32")
41   - private String subject;
42   - /**
43   - * 商品描述
44   - */
45   - @NotEmpty(message = "商品描述信息不能为空")
46   - @Length(max = 128, message = "商品描述信息长度不能超过128")
47   - private String body;
48   -
49   - // ========== 订单相关字段 ==========
50   -
51   - /**
52   - * 支付金额,单位:分
53   - */
54   - @NotNull(message = "支付金额不能为空")
55   - @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
56   - private Integer amount;
57   -
58   - /**
59   - * 支付过期时间
60   - */
61   - @NotNull(message = "支付过期时间不能为空")
62   - private LocalDateTime expireTime;
63   -
64   -}
yudao-server/src/main/java/cn/iocoder/yudao/module/shop/controller/app/AppShopOrderController.java
1 1 package cn.iocoder.yudao.module.shop.controller.app;
2 2  
3   -import cn.hutool.core.date.LocalDateTimeUtil;
4 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 4 import cn.iocoder.yudao.module.pay.service.notify.vo.PayNotifyOrderReqVO;
6 5 import cn.iocoder.yudao.module.pay.service.notify.vo.PayRefundOrderReqVO;
7 6 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
8   -import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderCreateReqDTO;
  7 +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
9 8 import cn.iocoder.yudao.module.pay.util.PaySeqUtils;
10 9 import cn.iocoder.yudao.module.shop.controller.app.vo.AppShopOrderCreateRespVO;
11 10 import io.swagger.annotations.Api;
... ... @@ -20,7 +19,6 @@ import org.springframework.web.bind.annotation.RestController;
20 19 import javax.annotation.Resource;
21 20 import javax.validation.Valid;
22 21 import java.time.LocalDateTime;
23   -import java.time.temporal.ChronoUnit;
24 22  
25 23 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
26 24 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
... ...
yudao-server/src/main/resources/application-dev.yaml
... ... @@ -167,9 +167,8 @@ yudao:
167 167 - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
168 168 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
169 169 pay:
170   - pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify
171   - pay-return-url: http://niubi.natapp1.cc/api/pay/order/return
172   - refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify
  170 + callback-url: http://yunai.natapp1.cc/admin-api/pay/notify/callback
  171 + return-url: http://yunai.natapp1.cc/admin-api/pay/notify/return
173 172 demo: true # 开启演示模式
174 173  
175 174 justauth:
... ...
yudao-server/src/main/resources/application-local.yaml
... ... @@ -191,9 +191,8 @@ yudao:
191 191 - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
192 192 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
193 193 pay:
194   - pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify
195   - pay-return-url: http://niubi.natapp1.cc/api/pay/order/return
196   - refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify
  194 + callback-url: http://yunai.natapp1.cc/admin-api/pay/notify/callback
  195 + return-url: http://yunai.natapp1.cc/admin-api/pay/notify/return
197 196 access-log: # 访问日志的配置项
198 197 enable: false
199 198 error-code: # 错误码相关配置项
... ...