优化 pay 支付成功后的回调逻辑
parent
73d847ae2e
commit
8798944069
|
@ -5,7 +5,7 @@
|
||||||
"adminTenentId": "1",
|
"adminTenentId": "1",
|
||||||
|
|
||||||
"appApi": "http://127.0.0.1:48080/app-api",
|
"appApi": "http://127.0.0.1:48080/app-api",
|
||||||
"appToken": "test1",
|
"appToken": "test247",
|
||||||
"appTenentId": "1"
|
"appTenentId": "1"
|
||||||
},
|
},
|
||||||
"gateway": {
|
"gateway": {
|
||||||
|
|
|
@ -13,26 +13,23 @@ import javax.validation.constraints.NotEmpty;
|
||||||
public class PayProperties {
|
public class PayProperties {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付回调地址
|
* 回调地址
|
||||||
|
*
|
||||||
|
* 实际上,对应的 PayNotifyController 的 notifyCallback 方法的 URL
|
||||||
|
*
|
||||||
* 注意,支付渠道统一回调到 payNotifyUrl 地址,由支付模块统一处理;然后,自己的支付模块,在回调 PayAppDO.payNotifyUrl 地址
|
* 注意,支付渠道统一回调到 payNotifyUrl 地址,由支付模块统一处理;然后,自己的支付模块,在回调 PayAppDO.payNotifyUrl 地址
|
||||||
*/
|
*/
|
||||||
@NotEmpty(message = "支付回调地址不能为空")
|
@NotEmpty(message = "回调地址不能为空")
|
||||||
@URL(message = "支付回调地址的格式必须是 URL")
|
@URL(message = "回调地址的格式必须是 URL")
|
||||||
private String payNotifyUrl;
|
private String callbackUrl;
|
||||||
/**
|
|
||||||
* 退款回调地址
|
|
||||||
* 注意点,同 {@link #payNotifyUrl} 属性
|
|
||||||
*/
|
|
||||||
@NotEmpty(message = "退款回调地址不能为空")
|
|
||||||
@URL(message = "退款回调地址的格式必须是 URL")
|
|
||||||
private String refundNotifyUrl;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付完成的返回地址
|
* 回跳地址
|
||||||
|
*
|
||||||
|
* 实际上,对应的 PayNotifyController 的 returnCallback 方法的 URL
|
||||||
*/
|
*/
|
||||||
@URL(message = "支付返回的地址的格式必须是 URL")
|
@URL(message = "回跳地址的格式必须是 URL")
|
||||||
@NotEmpty(message = "支付返回的地址不能为空")
|
@NotEmpty(message = "回跳地址不能为空")
|
||||||
private String payReturnUrl;
|
private String returnUrl;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,9 @@ public class PayNotifyDataDTO {
|
||||||
*/
|
*/
|
||||||
private String body;
|
private String body;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数
|
* HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数
|
||||||
*/
|
*/
|
||||||
private Map<String,String> params;
|
private Map<String,String> params;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,18 +8,20 @@ GET {{shop-api-base-url}}/trade-order/confirm-create-order-info-from-cart
|
||||||
Content-Type: application/x-www-form-urlencoded
|
Content-Type: application/x-www-form-urlencoded
|
||||||
Authorization: Bearer {{user-access-token}}
|
Authorization: Bearer {{user-access-token}}
|
||||||
|
|
||||||
### /trade-order/confirm-create-order-info-from-cart 基于商品,创建订单
|
### /trade-order/create 基于商品,创建订单
|
||||||
POST {{shop-api-base-url}}/trade-order/create
|
POST {{appApi}}/trade/order/create
|
||||||
Content-Type: application/json
|
Content-Type: application/json
|
||||||
Authorization: Bearer {{user-access-token}}
|
Authorization: Bearer {{appToken}}
|
||||||
|
tenant-id: {{appTenentId}}
|
||||||
|
|
||||||
{
|
{
|
||||||
"userAddressId": 19,
|
"addressId": 21,
|
||||||
"remark": "我是备注",
|
"remark": "我是备注",
|
||||||
"orderItems": [
|
"fromCart": false,
|
||||||
|
"items": [
|
||||||
{
|
{
|
||||||
"skuId": 3,
|
"skuId": 29,
|
||||||
"quantity": 1
|
"count": 1
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import io.swagger.annotations.ApiModel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
|
import javax.validation.Valid;
|
||||||
import javax.validation.constraints.Min;
|
import javax.validation.constraints.Min;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
|
@ -31,6 +32,7 @@ public class AppTradeOrderCreateReqVO {
|
||||||
* 订单商品项列表
|
* 订单商品项列表
|
||||||
*/
|
*/
|
||||||
@NotEmpty(message = "必须选择购买的商品")
|
@NotEmpty(message = "必须选择购买的商品")
|
||||||
|
@Valid
|
||||||
private List<Item> items;
|
private List<Item> items;
|
||||||
|
|
||||||
@ApiModel(value = "订单商品项")
|
@ApiModel(value = "订单商品项")
|
||||||
|
|
|
@ -30,6 +30,7 @@ public interface TradeOrderConvert {
|
||||||
TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class);
|
TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class);
|
||||||
|
|
||||||
@Mappings({
|
@Mappings({
|
||||||
|
@Mapping(target = "id", ignore = true),
|
||||||
@Mapping(source = "createReqVO.couponId", target = "couponId"),
|
@Mapping(source = "createReqVO.couponId", target = "couponId"),
|
||||||
@Mapping(target = "remark", ignore = true),
|
@Mapping(target = "remark", ignore = true),
|
||||||
@Mapping(source = "createReqVO.remark", target = "userRemark"),
|
@Mapping(source = "createReqVO.remark", target = "userRemark"),
|
||||||
|
|
|
@ -167,7 +167,7 @@ public class TradeOrderServiceImpl implements TradeOrderService {
|
||||||
* @return 收件地址
|
* @return 收件地址
|
||||||
*/
|
*/
|
||||||
private AddressRespDTO validateAddress(Long userId, Long addressId) {
|
private AddressRespDTO validateAddress(Long userId, Long addressId) {
|
||||||
AddressRespDTO address = addressApi.getAddress(userId, addressId);
|
AddressRespDTO address = addressApi.getAddress(addressId, userId);
|
||||||
if (Objects.isNull(address)) {
|
if (Objects.isNull(address)) {
|
||||||
throw exception(ErrorCodeConstants.ORDER_CREATE_ADDRESS_NOT_FOUND);
|
throw exception(ErrorCodeConstants.ORDER_CREATE_ADDRESS_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.pay.api.order.dto;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.hibernate.validator.constraints.Length;
|
import org.hibernate.validator.constraints.Length;
|
||||||
|
|
||||||
import javax.validation.constraints.Min;
|
import javax.validation.constraints.DecimalMin;
|
||||||
import javax.validation.constraints.NotEmpty;
|
import javax.validation.constraints.NotEmpty;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
@ -11,8 +11,6 @@ import java.time.LocalDateTime;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付单创建 Request DTO
|
* 支付单创建 Request DTO
|
||||||
*
|
|
||||||
* @author LeeYan9
|
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
public class PayOrderCreateReqDTO implements Serializable {
|
public class PayOrderCreateReqDTO implements Serializable {
|
||||||
|
@ -44,7 +42,7 @@ public class PayOrderCreateReqDTO implements Serializable {
|
||||||
/**
|
/**
|
||||||
* 商品描述
|
* 商品描述
|
||||||
*/
|
*/
|
||||||
// @NotEmpty(message = "商品描述信息不能为空") // 允许空
|
// @NotEmpty(message = "商品描述信息不能为空")
|
||||||
@Length(max = 128, message = "商品描述信息长度不能超过128")
|
@Length(max = 128, message = "商品描述信息长度不能超过128")
|
||||||
private String body;
|
private String body;
|
||||||
|
|
||||||
|
@ -54,7 +52,7 @@ public class PayOrderCreateReqDTO implements Serializable {
|
||||||
* 支付金额,单位:分
|
* 支付金额,单位:分
|
||||||
*/
|
*/
|
||||||
@NotNull(message = "支付金额不能为空")
|
@NotNull(message = "支付金额不能为空")
|
||||||
@Min(value = 1, message = "支付金额必须大于零")
|
@DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
|
||||||
private Integer amount;
|
private Integer amount;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,17 +1,25 @@
|
||||||
package cn.iocoder.yudao.module.pay.api.order;
|
package cn.iocoder.yudao.module.pay.api.order;
|
||||||
|
|
||||||
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
|
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
|
||||||
|
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO 注释
|
* 支付单 API 实现类
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
public class PayOrderApiImpl implements PayOrderApi {
|
public class PayOrderApiImpl implements PayOrderApi {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private PayOrderService payOrderService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long createPayOrder(PayOrderCreateReqDTO reqDTO) {
|
public Long createPayOrder(PayOrderCreateReqDTO reqDTO) {
|
||||||
return null;
|
return payOrderService.createPayOrder(reqDTO);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package cn.iocoder.yudao.module.pay.controller.admin.notify;
|
package cn.iocoder.yudao.module.pay.controller.admin.notify;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
|
||||||
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
|
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
|
||||||
import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
|
import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
|
||||||
import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
|
import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
|
||||||
|
@ -61,6 +62,7 @@ public class PayNotifyController {
|
||||||
@PostMapping(value = "/callback/{channelId}")
|
@PostMapping(value = "/callback/{channelId}")
|
||||||
@ApiOperation(value = "支付渠道的统一回调接口", notes = "包括支付回调,退款回调")
|
@ApiOperation(value = "支付渠道的统一回调接口", notes = "包括支付回调,退款回调")
|
||||||
@PermitAll
|
@PermitAll
|
||||||
|
@OperateLog(enable = false) // 回调地址,无需记录操作日志
|
||||||
public String notifyCallback(@PathVariable("channelId") Long channelId,
|
public String notifyCallback(@PathVariable("channelId") Long channelId,
|
||||||
@RequestParam Map<String, String> params,
|
@RequestParam Map<String, String> params,
|
||||||
@RequestBody String body) throws Exception {
|
@RequestBody String body) throws Exception {
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
### /pay/create 提交支付订单
|
||||||
|
POST {{appApi}}/pay/order/submit
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {{appToken}}
|
||||||
|
tenant-id: {{appTenentId}}
|
||||||
|
|
||||||
|
{
|
||||||
|
"id": 125,
|
||||||
|
"channelCode": "alipay_qr"
|
||||||
|
}
|
|
@ -1,16 +1,17 @@
|
||||||
package cn.iocoder.yudao.module.pay.convert.order;
|
package cn.iocoder.yudao.module.pay.convert.order;
|
||||||
|
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
|
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
|
||||||
|
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
|
||||||
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderDetailsRespVO;
|
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderDetailsRespVO;
|
||||||
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExcelVO;
|
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExcelVO;
|
||||||
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageItemRespVO;
|
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageItemRespVO;
|
||||||
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderRespVO;
|
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderRespVO;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
||||||
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
|
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
|
||||||
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
|
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO;
|
||||||
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderCreateReqDTO;
|
|
||||||
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO;
|
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO;
|
||||||
import org.mapstruct.Mapper;
|
import org.mapstruct.Mapper;
|
||||||
|
import org.mapstruct.Mapping;
|
||||||
import org.mapstruct.factory.Mappers;
|
import org.mapstruct.factory.Mappers;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
@ -88,6 +89,7 @@ public interface PayOrderConvert {
|
||||||
|
|
||||||
PayOrderDO convert(PayOrderCreateReqDTO bean);
|
PayOrderDO convert(PayOrderCreateReqDTO bean);
|
||||||
|
|
||||||
|
@Mapping(target = "id", ignore = true)
|
||||||
PayOrderExtensionDO convert(PayOrderSubmitReqDTO bean);
|
PayOrderExtensionDO convert(PayOrderSubmitReqDTO bean);
|
||||||
|
|
||||||
PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqDTO bean);
|
PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqDTO bean);
|
||||||
|
|
|
@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||||
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
|
import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO;
|
||||||
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderCreateReqDTO;
|
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
|
||||||
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO;
|
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO;
|
||||||
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitRespDTO;
|
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitRespDTO;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package cn.iocoder.yudao.module.pay.service.order;
|
package cn.iocoder.yudao.module.pay.service.order;
|
||||||
|
|
||||||
import cn.hutool.core.date.DateUtil;
|
import cn.hutool.core.date.DateUtil;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.RandomUtil;
|
import cn.hutool.core.util.RandomUtil;
|
||||||
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||||
import cn.iocoder.yudao.framework.pay.config.PayProperties;
|
import cn.iocoder.yudao.framework.pay.config.PayProperties;
|
||||||
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
|
import cn.iocoder.yudao.framework.pay.core.client.PayClient;
|
||||||
|
@ -11,6 +12,8 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
|
||||||
import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
|
import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO;
|
||||||
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO;
|
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO;
|
||||||
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
|
import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
|
||||||
|
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
|
||||||
|
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
|
||||||
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO;
|
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderExportReqVO;
|
||||||
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
|
import cn.iocoder.yudao.module.pay.controller.admin.order.vo.PayOrderPageReqVO;
|
||||||
import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert;
|
import cn.iocoder.yudao.module.pay.convert.order.PayOrderConvert;
|
||||||
|
@ -28,8 +31,6 @@ import cn.iocoder.yudao.module.pay.service.merchant.PayAppService;
|
||||||
import cn.iocoder.yudao.module.pay.service.merchant.PayChannelService;
|
import cn.iocoder.yudao.module.pay.service.merchant.PayChannelService;
|
||||||
import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
|
import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
|
||||||
import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;
|
import cn.iocoder.yudao.module.pay.service.notify.dto.PayNotifyTaskCreateReqDTO;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
||||||
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderCreateReqDTO;
|
|
||||||
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO;
|
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitReqDTO;
|
||||||
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitRespDTO;
|
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderSubmitRespDTO;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -43,6 +44,8 @@ import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 支付订单 Service 实现类
|
* 支付订单 Service 实现类
|
||||||
*
|
*
|
||||||
|
@ -133,16 +136,16 @@ public class PayOrderServiceImpl implements PayOrderService {
|
||||||
PayClient client = payClientFactory.getPayClient(channel.getId());
|
PayClient client = payClientFactory.getPayClient(channel.getId());
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
log.error("[submitPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
|
log.error("[submitPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
|
||||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
|
throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得 PayOrderDO ,并校验其是否存在
|
// 获得 PayOrderDO ,并校验其是否存在
|
||||||
PayOrderDO order = orderMapper.selectById(reqDTO.getId());
|
PayOrderDO order = orderMapper.selectById(reqDTO.getId());
|
||||||
if (order == null || !Objects.equals(order.getAppId(), reqDTO.getAppId())) { // 是否存在
|
if (order == null || !Objects.equals(order.getAppId(), reqDTO.getAppId())) { // 是否存在
|
||||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
|
throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
|
||||||
}
|
}
|
||||||
if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
|
if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
|
||||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
|
throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 插入 PayOrderExtensionDO
|
// 插入 PayOrderExtensionDO
|
||||||
|
@ -177,7 +180,7 @@ public class PayOrderServiceImpl implements PayOrderService {
|
||||||
* @return 支付成功返回的地址。 配置地址 + "/" + channel id
|
* @return 支付成功返回的地址。 配置地址 + "/" + channel id
|
||||||
*/
|
*/
|
||||||
private String genChannelReturnUrl(PayChannelDO channel) {
|
private String genChannelReturnUrl(PayChannelDO channel) {
|
||||||
return payProperties.getPayReturnUrl() + "/" + channel.getId();
|
return payProperties.getReturnUrl() + "/" + channel.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -187,8 +190,7 @@ public class PayOrderServiceImpl implements PayOrderService {
|
||||||
* @return 支付渠道的回调地址 配置地址 + "/" + channel id
|
* @return 支付渠道的回调地址 配置地址 + "/" + channel id
|
||||||
*/
|
*/
|
||||||
private String genChannelPayNotifyUrl(PayChannelDO channel) {
|
private String genChannelPayNotifyUrl(PayChannelDO channel) {
|
||||||
//去掉channel code, 似乎没啥用, 用统一的回调地址
|
return payProperties.getCallbackUrl() + "/" + channel.getId();
|
||||||
return payProperties.getPayNotifyUrl() + "/" + channel.getId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateOrderExtensionNo() {
|
private String generateOrderExtensionNo() {
|
||||||
|
@ -210,64 +212,99 @@ public class PayOrderServiceImpl implements PayOrderService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public void notifyPayOrder(Long channelId, PayNotifyDataDTO notifyData) throws Exception {
|
public void notifyPayOrder(Long channelId, PayNotifyDataDTO notifyData) {
|
||||||
// TODO 芋艿,记录回调日志
|
// TODO 芋艿,记录回调日志
|
||||||
log.info("[notifyPayOrder][channelId({}) 回调数据({})]", channelId, notifyData.getBody());
|
log.info("[notifyPayOrder][channelId({}) 回调数据({})]", channelId, notifyData.getBody());
|
||||||
|
|
||||||
// 校验支付渠道是否有效
|
// 校验支付渠道是否有效
|
||||||
PayChannelDO channel = channelService.validPayChannel(channelId);
|
PayChannelDO channel = channelService.validPayChannel(channelId);
|
||||||
|
TenantUtils.execute(channel.getTenantId(), () -> {
|
||||||
|
try {
|
||||||
|
notifyPayOrder(channel, notifyData);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyPayOrder(PayChannelDO channel, PayNotifyDataDTO notifyData) throws Exception {
|
||||||
// 校验支付客户端是否正确初始化
|
// 校验支付客户端是否正确初始化
|
||||||
PayClient client = payClientFactory.getPayClient(channel.getId());
|
PayClient client = payClientFactory.getPayClient(channel.getId());
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
log.error("[notifyPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
|
log.error("[notifyPayOrder][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
|
||||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
|
throw exception(ErrorCodeConstants.PAY_CHANNEL_CLIENT_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析支付结果
|
// 0. 解析支付结果
|
||||||
PayOrderNotifyRespDTO notifyRespDTO = client.parseOrderNotify(notifyData);
|
PayOrderNotifyRespDTO notifyRespDTO = client.parseOrderNotify(notifyData);
|
||||||
|
// 1. 更新 PayOrderExtensionDO 支付成功
|
||||||
// TODO 芋艿,先最严格的校验。即使调用方重复调用,实际哪个订单已经被重复回调的支付,也返回 false 。也没问题,因为实际已经回调成功了。
|
PayOrderExtensionDO orderExtension = updatePayOrderExtensionSuccess(notifyRespDTO.getOrderExtensionNo(), notifyData.getBody());
|
||||||
// 1.1 查询 PayOrderExtensionDO
|
// 2. 更新 PayOrderDO 支付成功
|
||||||
PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(notifyRespDTO.getOrderExtensionNo());
|
PayOrderDO order = updatePayOrderSuccess(channel, orderExtension, notifyRespDTO);
|
||||||
if (orderExtension == null) {
|
|
||||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND);
|
|
||||||
}
|
|
||||||
if (!PayOrderStatusEnum.WAITING.getStatus().equals(orderExtension.getStatus())) { // 校验状态,必须是待支付
|
|
||||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
|
|
||||||
}
|
|
||||||
// 1.2 更新 PayOrderExtensionDO
|
|
||||||
//TODO 支付宝交易超时 TRADE_FINISHED 需要更新交易关闭
|
|
||||||
int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(),
|
|
||||||
PayOrderStatusEnum.WAITING.getStatus(), PayOrderExtensionDO.builder().id(orderExtension.getId())
|
|
||||||
.status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(notifyData.getBody()).build());
|
|
||||||
if (updateCounts == 0) { // 校验状态,必须是待支付
|
|
||||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
|
|
||||||
}
|
|
||||||
log.info("[notifyPayOrder][支付拓展单({}) 更新为已支付]", orderExtension.getId());
|
|
||||||
|
|
||||||
// 2.1 判断 PayOrderDO 是否处于待支付
|
|
||||||
PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
|
|
||||||
if (order == null) {
|
|
||||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
|
|
||||||
}
|
|
||||||
if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
|
|
||||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
|
|
||||||
}
|
|
||||||
// 2.2 更新 PayOrderDO
|
|
||||||
updateCounts = orderMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(),
|
|
||||||
PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus()).channelId(channelId).channelCode(channel.getCode())
|
|
||||||
.successTime(notifyRespDTO.getSuccessTime()).successExtensionId(orderExtension.getId())
|
|
||||||
.channelOrderNo(notifyRespDTO.getChannelOrderNo()).channelUserId(notifyRespDTO.getChannelUserId())
|
|
||||||
.notifyTime(LocalDateTime.now()).build());
|
|
||||||
if (updateCounts == 0) { // 校验状态,必须是待支付
|
|
||||||
throw ServiceExceptionUtil.exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
|
|
||||||
}
|
|
||||||
log.info("[notifyPayOrder][支付订单({}) 更新为已支付]", order.getId());
|
|
||||||
|
|
||||||
// 3. 插入支付通知记录
|
// 3. 插入支付通知记录
|
||||||
notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()
|
notifyService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder()
|
||||||
.type(PayNotifyTypeEnum.ORDER.getType()).dataId(order.getId()).build());
|
.type(PayNotifyTypeEnum.ORDER.getType()).dataId(order.getId()).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新 PayOrderExtensionDO 支付成功
|
||||||
|
*
|
||||||
|
* @param no 支付订单号(支付模块)
|
||||||
|
* @param body 回调内容
|
||||||
|
* @return PayOrderExtensionDO 对象
|
||||||
|
*/
|
||||||
|
private PayOrderExtensionDO updatePayOrderExtensionSuccess(String no, String body) {
|
||||||
|
// 1.1 查询 PayOrderExtensionDO
|
||||||
|
PayOrderExtensionDO orderExtension = orderExtensionMapper.selectByNo(no);
|
||||||
|
if (orderExtension == null) {
|
||||||
|
throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_NOT_FOUND);
|
||||||
|
}
|
||||||
|
if (ObjectUtil.notEqual(orderExtension.getStatus(), PayOrderStatusEnum.WAITING.getStatus())) { // 校验状态,必须是待支付
|
||||||
|
throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
|
||||||
|
}
|
||||||
|
// 1.2 更新 PayOrderExtensionDO
|
||||||
|
int updateCounts = orderExtensionMapper.updateByIdAndStatus(orderExtension.getId(),
|
||||||
|
PayOrderStatusEnum.WAITING.getStatus(), PayOrderExtensionDO.builder().id(orderExtension.getId())
|
||||||
|
.status(PayOrderStatusEnum.SUCCESS.getStatus()).channelNotifyData(body).build());
|
||||||
|
if (updateCounts == 0) { // 校验状态,必须是待支付
|
||||||
|
throw exception(ErrorCodeConstants.PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING);
|
||||||
|
}
|
||||||
|
log.info("[updatePayOrderSuccess][支付拓展单({}) 更新为已支付]", orderExtension.getId());
|
||||||
|
return orderExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新 PayOrderDO 支付成功
|
||||||
|
*
|
||||||
|
* @param channel 支付渠道
|
||||||
|
* @param orderExtension 支付拓展单
|
||||||
|
* @param notifyRespDTO 通知回调
|
||||||
|
* @return PayOrderDO 对象
|
||||||
|
*/
|
||||||
|
private PayOrderDO updatePayOrderSuccess(PayChannelDO channel, PayOrderExtensionDO orderExtension,
|
||||||
|
PayOrderNotifyRespDTO notifyRespDTO) {
|
||||||
|
// 2.1 判断 PayOrderDO 是否处于待支付
|
||||||
|
PayOrderDO order = orderMapper.selectById(orderExtension.getOrderId());
|
||||||
|
if (order == null) {
|
||||||
|
throw exception(ErrorCodeConstants.PAY_ORDER_NOT_FOUND);
|
||||||
|
}
|
||||||
|
if (!PayOrderStatusEnum.WAITING.getStatus().equals(order.getStatus())) { // 校验状态,必须是待支付
|
||||||
|
throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
|
||||||
|
}
|
||||||
|
// 2.2 更新 PayOrderDO
|
||||||
|
int updateCounts = orderMapper.updateByIdAndStatus(order.getId(), PayOrderStatusEnum.WAITING.getStatus(),
|
||||||
|
PayOrderDO.builder().status(PayOrderStatusEnum.SUCCESS.getStatus())
|
||||||
|
.channelId(channel.getId()).channelCode(channel.getCode())
|
||||||
|
.successTime(notifyRespDTO.getSuccessTime()).successExtensionId(orderExtension.getId())
|
||||||
|
.channelOrderNo(notifyRespDTO.getChannelOrderNo()).channelUserId(notifyRespDTO.getChannelUserId())
|
||||||
|
.notifyTime(LocalDateTime.now()).build());
|
||||||
|
if (updateCounts == 0) { // 校验状态,必须是待支付
|
||||||
|
throw exception(ErrorCodeConstants.PAY_ORDER_STATUS_IS_NOT_WAITING);
|
||||||
|
}
|
||||||
|
log.info("[updatePayOrderSuccess][支付订单({}) 更新为已支付]", order.getId());
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
package cn.iocoder.yudao.module.pay.service.order.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import org.hibernate.validator.constraints.Length;
|
|
||||||
|
|
||||||
import javax.validation.constraints.DecimalMin;
|
|
||||||
import javax.validation.constraints.NotEmpty;
|
|
||||||
import javax.validation.constraints.NotNull;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付单创建 Request DTO
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class PayOrderCreateReqDTO implements Serializable {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 应用编号
|
|
||||||
*/
|
|
||||||
@NotNull(message = "应用编号不能为空")
|
|
||||||
private Long appId;
|
|
||||||
/**
|
|
||||||
* 用户 IP
|
|
||||||
*/
|
|
||||||
@NotEmpty(message = "用户 IP 不能为空")
|
|
||||||
private String userIp;
|
|
||||||
|
|
||||||
// ========== 商户相关字段 ==========
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 商户订单编号
|
|
||||||
*/
|
|
||||||
@NotEmpty(message = "商户订单编号不能为空")
|
|
||||||
private String merchantOrderId;
|
|
||||||
/**
|
|
||||||
* 商品标题
|
|
||||||
*/
|
|
||||||
@NotEmpty(message = "商品标题不能为空")
|
|
||||||
@Length(max = 32, message = "商品标题不能超过 32")
|
|
||||||
private String subject;
|
|
||||||
/**
|
|
||||||
* 商品描述
|
|
||||||
*/
|
|
||||||
@NotEmpty(message = "商品描述信息不能为空")
|
|
||||||
@Length(max = 128, message = "商品描述信息长度不能超过128")
|
|
||||||
private String body;
|
|
||||||
|
|
||||||
// ========== 订单相关字段 ==========
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付金额,单位:分
|
|
||||||
*/
|
|
||||||
@NotNull(message = "支付金额不能为空")
|
|
||||||
@DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零")
|
|
||||||
private Integer amount;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 支付过期时间
|
|
||||||
*/
|
|
||||||
@NotNull(message = "支付过期时间不能为空")
|
|
||||||
private LocalDateTime expireTime;
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,11 +1,10 @@
|
||||||
package cn.iocoder.yudao.module.shop.controller.app;
|
package cn.iocoder.yudao.module.shop.controller.app;
|
||||||
|
|
||||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||||
import cn.iocoder.yudao.module.pay.service.notify.vo.PayNotifyOrderReqVO;
|
import cn.iocoder.yudao.module.pay.service.notify.vo.PayNotifyOrderReqVO;
|
||||||
import cn.iocoder.yudao.module.pay.service.notify.vo.PayRefundOrderReqVO;
|
import cn.iocoder.yudao.module.pay.service.notify.vo.PayRefundOrderReqVO;
|
||||||
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
|
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
|
||||||
import cn.iocoder.yudao.module.pay.service.order.dto.PayOrderCreateReqDTO;
|
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
|
||||||
import cn.iocoder.yudao.module.pay.util.PaySeqUtils;
|
import cn.iocoder.yudao.module.pay.util.PaySeqUtils;
|
||||||
import cn.iocoder.yudao.module.shop.controller.app.vo.AppShopOrderCreateRespVO;
|
import cn.iocoder.yudao.module.shop.controller.app.vo.AppShopOrderCreateRespVO;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
|
@ -20,7 +19,6 @@ import org.springframework.web.bind.annotation.RestController;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import javax.validation.Valid;
|
import javax.validation.Valid;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.temporal.ChronoUnit;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||||
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
|
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
|
||||||
|
|
|
@ -167,9 +167,8 @@ yudao:
|
||||||
- ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
|
- ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
|
||||||
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
|
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
|
||||||
pay:
|
pay:
|
||||||
pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify
|
callback-url: http://yunai.natapp1.cc/admin-api/pay/notify/callback
|
||||||
pay-return-url: http://niubi.natapp1.cc/api/pay/order/return
|
return-url: http://yunai.natapp1.cc/admin-api/pay/notify/return
|
||||||
refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify
|
|
||||||
demo: true # 开启演示模式
|
demo: true # 开启演示模式
|
||||||
|
|
||||||
justauth:
|
justauth:
|
||||||
|
|
|
@ -191,9 +191,8 @@ yudao:
|
||||||
- ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
|
- ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
|
||||||
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
|
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
|
||||||
pay:
|
pay:
|
||||||
pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify
|
callback-url: http://yunai.natapp1.cc/admin-api/pay/notify/callback
|
||||||
pay-return-url: http://niubi.natapp1.cc/api/pay/order/return
|
return-url: http://yunai.natapp1.cc/admin-api/pay/notify/return
|
||||||
refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify
|
|
||||||
access-log: # 访问日志的配置项
|
access-log: # 访问日志的配置项
|
||||||
enable: false
|
enable: false
|
||||||
error-code: # 错误码相关配置项
|
error-code: # 错误码相关配置项
|
||||||
|
|
Loading…
Reference in New Issue