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,7 +5,7 @@
5 "adminTenentId": "1", 5 "adminTenentId": "1",
6 6
7 "appApi": "http://127.0.0.1:48080/app-api", 7 "appApi": "http://127.0.0.1:48080/app-api",
8 - "appToken": "test1", 8 + "appToken": "test247",
9 "appTenentId": "1" 9 "appTenentId": "1"
10 }, 10 },
11 "gateway": { 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,26 +13,23 @@ import javax.validation.constraints.NotEmpty;
13 public class PayProperties { 13 public class PayProperties {
14 14
15 /** 15 /**
16 - * 支付回调地址 16 + * 回调地址
  17 + *
  18 + * 实际上,对应的 PayNotifyController 的 notifyCallback 方法的 URL
  19 + *
17 * 注意,支付渠道统一回调到 payNotifyUrl 地址,由支付模块统一处理;然后,自己的支付模块,在回调 PayAppDO.payNotifyUrl 地址 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,9 +21,9 @@ public class PayNotifyDataDTO {
21 */ 21 */
22 private String body; 22 private String body;
23 23
24 -  
25 /** 24 /**
26 * HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数 25 * HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数
27 */ 26 */
28 private Map<String,String> params; 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,18 +8,20 @@ GET {{shop-api-base-url}}/trade-order/confirm-create-order-info-from-cart
8 Content-Type: application/x-www-form-urlencoded 8 Content-Type: application/x-www-form-urlencoded
9 Authorization: Bearer {{user-access-token}} 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 Content-Type: application/json 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 "remark": "我是备注", 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,6 +4,7 @@ import io.swagger.annotations.ApiModel;
4 import io.swagger.annotations.ApiModelProperty; 4 import io.swagger.annotations.ApiModelProperty;
5 import lombok.Data; 5 import lombok.Data;
6 6
  7 +import javax.validation.Valid;
7 import javax.validation.constraints.Min; 8 import javax.validation.constraints.Min;
8 import javax.validation.constraints.NotEmpty; 9 import javax.validation.constraints.NotEmpty;
9 import javax.validation.constraints.NotNull; 10 import javax.validation.constraints.NotNull;
@@ -31,6 +32,7 @@ public class AppTradeOrderCreateReqVO { @@ -31,6 +32,7 @@ public class AppTradeOrderCreateReqVO {
31 * 订单商品项列表 32 * 订单商品项列表
32 */ 33 */
33 @NotEmpty(message = "必须选择购买的商品") 34 @NotEmpty(message = "必须选择购买的商品")
  35 + @Valid
34 private List<Item> items; 36 private List<Item> items;
35 37
36 @ApiModel(value = "订单商品项") 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,6 +30,7 @@ public interface TradeOrderConvert {
30 TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class); 30 TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class);
31 31
32 @Mappings({ 32 @Mappings({
  33 + @Mapping(target = "id", ignore = true),
33 @Mapping(source = "createReqVO.couponId", target = "couponId"), 34 @Mapping(source = "createReqVO.couponId", target = "couponId"),
34 @Mapping(target = "remark", ignore = true), 35 @Mapping(target = "remark", ignore = true),
35 @Mapping(source = "createReqVO.remark", target = "userRemark"), 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,7 +167,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
167 * @return 收件地址 167 * @return 收件地址
168 */ 168 */
169 private AddressRespDTO validateAddress(Long userId, Long addressId) { 169 private AddressRespDTO validateAddress(Long userId, Long addressId) {
170 - AddressRespDTO address = addressApi.getAddress(userId, addressId); 170 + AddressRespDTO address = addressApi.getAddress(addressId, userId);
171 if (Objects.isNull(address)) { 171 if (Objects.isNull(address)) {
172 throw exception(ErrorCodeConstants.ORDER_CREATE_ADDRESS_NOT_FOUND); 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,7 +3,7 @@ package cn.iocoder.yudao.module.pay.api.order.dto;
3 import lombok.Data; 3 import lombok.Data;
4 import org.hibernate.validator.constraints.Length; 4 import org.hibernate.validator.constraints.Length;
5 5
6 -import javax.validation.constraints.Min; 6 +import javax.validation.constraints.DecimalMin;
7 import javax.validation.constraints.NotEmpty; 7 import javax.validation.constraints.NotEmpty;
8 import javax.validation.constraints.NotNull; 8 import javax.validation.constraints.NotNull;
9 import java.io.Serializable; 9 import java.io.Serializable;
@@ -11,8 +11,6 @@ import java.time.LocalDateTime; @@ -11,8 +11,6 @@ import java.time.LocalDateTime;
11 11
12 /** 12 /**
13 * 支付单创建 Request DTO 13 * 支付单创建 Request DTO
14 - *  
15 - * @author LeeYan9  
16 */ 14 */
17 @Data 15 @Data
18 public class PayOrderCreateReqDTO implements Serializable { 16 public class PayOrderCreateReqDTO implements Serializable {
@@ -44,7 +42,7 @@ 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 @Length(max = 128, message = "商品描述信息长度不能超过128") 46 @Length(max = 128, message = "商品描述信息长度不能超过128")
49 private String body; 47 private String body;
50 48
@@ -54,7 +52,7 @@ public class PayOrderCreateReqDTO implements Serializable { @@ -54,7 +52,7 @@ public class PayOrderCreateReqDTO implements Serializable {
54 * 支付金额,单位:分 52 * 支付金额,单位:分
55 */ 53 */
56 @NotNull(message = "支付金额不能为空") 54 @NotNull(message = "支付金额不能为空")
57 - @Min(value = 1, message = "支付金额必须大于零") 55 + @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
58 private Integer amount; 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 package cn.iocoder.yudao.module.pay.api.order; 1 package cn.iocoder.yudao.module.pay.api.order;
2 2
3 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; 3 import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
  4 +import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
4 import org.springframework.stereotype.Service; 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 @Service 14 @Service
10 public class PayOrderApiImpl implements PayOrderApi { 15 public class PayOrderApiImpl implements PayOrderApi {
11 16
  17 + @Resource
  18 + private PayOrderService payOrderService;
  19 +
12 @Override 20 @Override
13 public Long createPayOrder(PayOrderCreateReqDTO reqDTO) { 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 package cn.iocoder.yudao.module.pay.controller.admin.notify; 1 package cn.iocoder.yudao.module.pay.controller.admin.notify;
2 2
  3 +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
3 import cn.iocoder.yudao.framework.pay.core.client.PayClient; 4 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
4 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; 5 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
5 import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO; 6 import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
@@ -61,6 +62,7 @@ public class PayNotifyController { @@ -61,6 +62,7 @@ public class PayNotifyController {
61 @PostMapping(value = "/callback/{channelId}") 62 @PostMapping(value = "/callback/{channelId}")
62 @ApiOperation(value = "支付渠道的统一回调接口", notes = "包括支付回调,退款回调") 63 @ApiOperation(value = "支付渠道的统一回调接口", notes = "包括支付回调,退款回调")
63 @PermitAll 64 @PermitAll
  65 + @OperateLog(enable = false) // 回调地址,无需记录操作日志
64 public String notifyCallback(@PathVariable("channelId") Long channelId, 66 public String notifyCallback(@PathVariable("channelId") Long channelId,
65 @RequestParam Map<String, String> params, 67 @RequestParam Map<String, String> params,
66 @RequestBody String body) throws Exception { 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 package cn.iocoder.yudao.module.pay.convert.order; 1 package cn.iocoder.yudao.module.pay.convert.order;
2 2
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
3 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; 4 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
  5 +import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
4 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderDetailsRespVO; 6 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderDetailsRespVO;
5 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExcelVO; 7 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExcelVO;
6 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageItemRespVO; 8 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageItemRespVO;
7 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderRespVO; 9 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderRespVO;
8 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
9 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; 10 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
10 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; 11 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
11 -import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderCreateReqDTO;  
12 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO; 12 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO;
13 import org.mapstruct.Mapper; 13 import org.mapstruct.Mapper;
  14 +import org.mapstruct.Mapping;
14 import org.mapstruct.factory.Mappers; 15 import org.mapstruct.factory.Mappers;
15 16
16 import java.math.BigDecimal; 17 import java.math.BigDecimal;
@@ -88,6 +89,7 @@ public interface PayOrderConvert { @@ -88,6 +89,7 @@ public interface PayOrderConvert {
88 89
89 PayOrderDO convert(PayOrderCreateReqDTO bean); 90 PayOrderDO convert(PayOrderCreateReqDTO bean);
90 91
  92 + @Mapping(target = "id", ignore = true)
91 PayOrderExtensionDO convert(PayOrderSubmitReqDTO bean); 93 PayOrderExtensionDO convert(PayOrderSubmitReqDTO bean);
92 94
93 PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqDTO bean); 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,7 +6,7 @@ import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
6 import cn.iocoder.yudao.framework.common.pojo.PageResult; 6 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; 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 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO; 10 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO;
11 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitRespDTO; 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 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.util.ObjectUtil;
4 import cn.hutool.core.util.RandomUtil; 5 import cn.hutool.core.util.RandomUtil;
5 -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;  
6 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 6 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  7 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 8 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
8 import cn.iocoder.yudao.framework.pay.config.PayProperties; 9 import cn.iocoder.yudao.framework.pay.config.PayProperties;
9 import cn.iocoder.yudao.framework.pay.core.client.PayClient; 10 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
@@ -11,6 +12,8 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; @@ -11,6 +12,8 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
11 import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO; 12 import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
12 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO; 13 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO;
13 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; 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 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO; 17 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO;
15 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO; 18 import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
16 import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert; 19 import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert;
@@ -28,8 +31,6 @@ import cn.iocoder.yudao.module.pay.service.merchant.PayAppService; @@ -28,8 +31,6 @@ import cn.iocoder.yudao.module.pay.service.merchant.PayAppService;
28 import cn.iocoder.yudao.module.pay.service.merchant.PayChannelService; 31 import cn.iocoder.yudao.module.pay.service.merchant.PayChannelService;
29 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; 32 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
30 import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO; 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 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO; 34 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO;
34 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitRespDTO; 35 import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitRespDTO;
35 import lombok.extern.slf4j.Slf4j; 36 import lombok.extern.slf4j.Slf4j;
@@ -43,6 +44,8 @@ import java.util.Collection; @@ -43,6 +44,8 @@ import java.util.Collection;
43 import java.util.List; 44 import java.util.List;
44 import java.util.Objects; 45 import java.util.Objects;
45 46
  47 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  48 +
46 /** 49 /**
47 * 支付订单 Service 实现类 50 * 支付订单 Service 实现类
48 * 51 *
@@ -133,16 +136,16 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -133,16 +136,16 @@ public class PayOrderServiceImpl implements PayOrderService {
133 PayClient client = payClientFactory.getPayClient(channel.getId()); 136 PayClient client = payClientFactory.getPayClient(channel.getId());
134 if (client == null) { 137 if (client == null) {
135 log.error("[submitPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); 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 // 获得 PayOrderDO ,并校验其是否存在 142 // 获得 PayOrderDO ,并校验其是否存在
140 PayOrderDO order = orderMapper.selectById(reqDTO.getId()); 143 PayOrderDO order = orderMapper.selectById(reqDTO.getId());
141 if (order == null || !Objects.equals(order.getAppId(), reqDTO.getAppId())) { // 是否存在 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 if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 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 // 插入 PayOrderExtensionDO 151 // 插入 PayOrderExtensionDO
@@ -177,7 +180,7 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -177,7 +180,7 @@ public class PayOrderServiceImpl implements PayOrderService {
177 * @return 支付成功返回的地址。 配置地址 + "/" + channel id 180 * @return 支付成功返回的地址。 配置地址 + "/" + channel id
178 */ 181 */
179 private String genChannelReturnUrl(PayChannelDO channel) { 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,8 +190,7 @@ public class PayOrderServiceImpl implements PayOrderService {
187 * @return 支付渠道的回调地址 配置地址 + "/" + channel id 190 * @return 支付渠道的回调地址 配置地址 + "/" + channel id
188 */ 191 */
189 private String genChannelPayNotifyUrl(PayChannelDO channel) { 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 private String generateOrderExtensionNo() { 196 private String generateOrderExtensionNo() {
@@ -210,64 +212,99 @@ public class PayOrderServiceImpl implements PayOrderService { @@ -210,64 +212,99 @@ public class PayOrderServiceImpl implements PayOrderService {
210 } 212 }
211 213
212 @Override 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 // TODO 芋艿,记录回调日志 217 // TODO 芋艿,记录回调日志
216 log.info("[notifyPayOrder][channelId({}) 回调数据({})]", channelId, notifyData.getBody()); 218 log.info("[notifyPayOrder][channelId({}) 回调数据({})]", channelId, notifyData.getBody());
217 219
218 // 校验支付渠道是否有效 220 // 校验支付渠道是否有效
219 PayChannelDO channel = channelService.validPayChannel(channelId); 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 PayClient client = payClientFactory.getPayClient(channel.getId()); 233 PayClient client = payClientFactory.getPayClient(channel.getId());
222 if (client == null) { 234 if (client == null) {
223 log.error("[notifyPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); 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 PayOrderNotifyRespDTO notifyRespDTO = client.parseOrderNotify(notifyData); 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 // 1.1 查询 PayOrderExtensionDO 259 // 1.1 查询 PayOrderExtensionDO
232 - PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notifyRespDTO.getOrderExtensionNo()); 260 + PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(no);
233 if (orderExtension == null) { 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 // 1.2 更新 PayOrderExtensionDO 267 // 1.2 更新 PayOrderExtensionDO
240 - //TODO 支付宝交易超时 TRADE_FINISHED 需要更新交易关闭  
241 int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(), 268 int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(),
242 PayOrderStatusEnum.WAITING.getStatus(), PayOrderExtensionDO.builder().id(orderExtension.getId()) 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 if (updateCounts == 0) { // 校验状态,必须是待支付 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 // 2.1 判断 PayOrderDO 是否处于待支付 288 // 2.1 判断 PayOrderDO 是否处于待支付
250 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId()); 289 PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
251 if (order == null) { 290 if (order == null) {
252 - throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND); 291 + throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
253 } 292 }
254 if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付 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 // 2.2 更新 PayOrderDO 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 .successTime(notifyRespDTO.getSuccessTime()).successExtensionId(orderExtension.getId()) 300 .successTime(notifyRespDTO.getSuccessTime()).successExtensionId(orderExtension.getId())
261 .channelOrderNo(notifyRespDTO.getChannelOrderNo()).channelUserId(notifyRespDTO.getChannelUserId()) 301 .channelOrderNo(notifyRespDTO.getChannelOrderNo()).channelUserId(notifyRespDTO.getChannelUserId())
262 .notifyTime(LocalDateTime.now()).build()); 302 .notifyTime(LocalDateTime.now()).build());
263 if (updateCounts == 0) { // 校验状态,必须是待支付 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 package cn.iocoder.yudao.module.shop.controller.app; 1 package cn.iocoder.yudao.module.shop.controller.app;
2 2
3 -import cn.hutool.core.date.LocalDateTimeUtil;  
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.module.pay.service.notify.vo.PayNotifyOrderReqVO; 4 import cn.iocoder.yudao.module.pay.service.notify.vo.PayNotifyOrderReqVO;
6 import cn.iocoder.yudao.module.pay.service.notify.vo.PayRefundOrderReqVO; 5 import cn.iocoder.yudao.module.pay.service.notify.vo.PayRefundOrderReqVO;
7 import cn.iocoder.yudao.module.pay.service.order.PayOrderService; 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 import cn.iocoder.yudao.module.pay.util.PaySeqUtils; 8 import cn.iocoder.yudao.module.pay.util.PaySeqUtils;
10 import cn.iocoder.yudao.module.shop.controller.app.vo.AppShopOrderCreateRespVO; 9 import cn.iocoder.yudao.module.shop.controller.app.vo.AppShopOrderCreateRespVO;
11 import io.swagger.annotations.Api; 10 import io.swagger.annotations.Api;
@@ -20,7 +19,6 @@ import org.springframework.web.bind.annotation.RestController; @@ -20,7 +19,6 @@ import org.springframework.web.bind.annotation.RestController;
20 import javax.annotation.Resource; 19 import javax.annotation.Resource;
21 import javax.validation.Valid; 20 import javax.validation.Valid;
22 import java.time.LocalDateTime; 21 import java.time.LocalDateTime;
23 -import java.time.temporal.ChronoUnit;  
24 22
25 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 23 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
26 import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; 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,9 +167,8 @@ yudao:
167 - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 167 - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
168 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 168 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
169 pay: 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 demo: true # 开启演示模式 172 demo: true # 开启演示模式
174 173
175 justauth: 174 justauth:
yudao-server/src/main/resources/application-local.yaml
@@ -191,9 +191,8 @@ yudao: @@ -191,9 +191,8 @@ yudao:
191 - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 191 - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
192 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 192 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
193 pay: 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 access-log: # 访问日志的配置项 196 access-log: # 访问日志的配置项
198 enable: false 197 enable: false
199 error-code: # 错误码相关配置项 198 error-code: # 错误码相关配置项