|
@@ -0,0 +1,830 @@
|
|
|
+package com.yonge.cooleshow.biz.dal.service.impl;
|
|
|
+
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
+import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
|
|
+import com.google.common.collect.Lists;
|
|
|
+import com.google.common.collect.Maps;
|
|
|
+import com.microsvc.toolkit.common.response.paging.QueryInfo;
|
|
|
+import com.microsvc.toolkit.common.spring.SpringContextHolder;
|
|
|
+import com.microsvc.toolkit.common.tools.ThreadPool;
|
|
|
+import com.microsvc.toolkit.common.webportal.exception.BizException;
|
|
|
+import com.microsvc.toolkit.config.jwt.utils.JwtUserInfo;
|
|
|
+import com.microsvc.toolkit.middleware.payment.common.api.BasePaymentService;
|
|
|
+import com.microsvc.toolkit.middleware.payment.common.api.PaymentServiceContext;
|
|
|
+import com.microsvc.toolkit.middleware.payment.common.api.entity.PaymentClose;
|
|
|
+import com.microsvc.toolkit.middleware.payment.common.api.entity.PaymentOrder;
|
|
|
+import com.microsvc.toolkit.middleware.payment.common.api.entity.PaymentReq;
|
|
|
+import com.microsvc.toolkit.middleware.payment.common.api.entity.PaymentResp;
|
|
|
+import com.microsvc.toolkit.middleware.payment.common.api.enums.PaymentStatus;
|
|
|
+import com.yonge.cooleshow.biz.dal.entity.UserOrderDetail;
|
|
|
+import com.yonge.cooleshow.biz.dal.enums.OrderStatusEnum;
|
|
|
+import com.yonge.cooleshow.biz.dal.service.*;
|
|
|
+import com.yonge.cooleshow.biz.dal.wrapper.UserPaymentOrderWrapper;
|
|
|
+import com.yonge.cooleshow.common.enums.ContractTemplateTypeEnum;
|
|
|
+import com.yonge.cooleshow.common.enums.SysUserType;
|
|
|
+import com.yonge.cooleshow.common.enums.payment.EGoodsType;
|
|
|
+import com.yonge.cooleshow.common.enums.payment.EPaymentChannel;
|
|
|
+import com.yonge.cooleshow.common.enums.payment.EPaymentStatus;
|
|
|
+import com.yonge.cooleshow.common.enums.payment.EPaymentType;
|
|
|
+import com.yonge.toolset.payment.util.DistributedLock;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.collections.CollectionUtils;
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
+import org.joda.time.DateTime;
|
|
|
+import org.redisson.api.RedissonClient;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import javax.annotation.PostConstruct;
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
+import java.util.*;
|
|
|
+import java.util.concurrent.TimeUnit;
|
|
|
+import java.util.function.Consumer;
|
|
|
+import java.util.function.Function;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+import java.util.stream.IntStream;
|
|
|
+
|
|
|
+import static com.yonge.cooleshow.common.enums.payment.EPaymentStatus.*;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 用户支付订单服务
|
|
|
+ * Created by Eric.Shang on 2022/12/22.
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class UserPaymentCoreServiceImpl implements UserPaymentCoreService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private RedissonClient redissonClient;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private PaymentServiceContext paymentServiceContext;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private RedisCacheService redisCacheService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private SysUserContractRecordService sysUserContractRecordService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private SysUserService sysUserService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private UserPaymentOrderService userPaymentOrderService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private UserOrderDetailService userOrderDetailService;
|
|
|
+
|
|
|
+
|
|
|
+ // 订单商品参数校验,获取订单支付金额
|
|
|
+ private static final Map<EGoodsType, Consumer<UserOrderDetail>> orderGoodsCreate = Maps.newHashMap();
|
|
|
+ // 支付订单类型参数校验
|
|
|
+ private static final Map<EPaymentType, Consumer<UserPaymentOrderWrapper.UserPaymentOrder>> orderCreate = Maps.newHashMap();
|
|
|
+ // 支付创建前,数据库存
|
|
|
+ private static final Map<EPaymentType, Consumer<UserPaymentOrderWrapper.UserPaymentOrder>> orderSuccessBefore = Maps.newHashMap();
|
|
|
+ // 支付创建前,数据库存
|
|
|
+// private static final Map<EPaymentType, Consumer<UserPaymentOrder>> executeSuccessBefore = Maps.newHashMap();
|
|
|
+ // 订单完成后执行
|
|
|
+ private static final Map<EPaymentType, Consumer<UserPaymentOrderWrapper.UserPaymentOrder>> paymentSuccess = Maps.newHashMap();
|
|
|
+
|
|
|
+ @PostConstruct
|
|
|
+ private void init() {
|
|
|
+
|
|
|
+ /*订单参数校验*/
|
|
|
+
|
|
|
+ /*支付订单类型参数校验*/
|
|
|
+
|
|
|
+ /*订单创建前处理流程*/
|
|
|
+
|
|
|
+ /*订单支付完成后执行*/
|
|
|
+
|
|
|
+ /*退款订单类型参数校验*/
|
|
|
+
|
|
|
+ /*退款支付完成执行*/
|
|
|
+
|
|
|
+ log.info("---> orderCreate.Keys={}", orderGoodsCreate.keySet());
|
|
|
+ log.info("---> paymentSuccess.Keys={}", paymentSuccess.keySet());
|
|
|
+ log.info("---> orderSuccessBefore.Keys={}", orderSuccessBefore.keySet());
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 支付回调消息
|
|
|
+ *
|
|
|
+ * @param paymentResp PaymentResp
|
|
|
+ *
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ @Override
|
|
|
+ public void executePaymentCallback(PaymentResp paymentResp) {
|
|
|
+
|
|
|
+ String lockName = redisCacheService.getPaymentCacheKey(paymentResp.getMerOrderNo());
|
|
|
+ // 订单分布式锁状态
|
|
|
+ DistributedLock.of(redissonClient).runIfLockCanGet(lockName, () -> {
|
|
|
+
|
|
|
+ // 获取订单信息
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrder paymentOrder = userPaymentOrderService.getUserPaymentOrderByOrderNo(paymentResp.getTransNo(), paymentResp.getMerOrderNo());
|
|
|
+ if (Objects.isNull(paymentOrder)) {
|
|
|
+ log.error("executePaymentCallback 无效的支付订单, paymentResp={}", JSON.toJSONString(paymentResp));
|
|
|
+ throw new BizException("无效的支付订单");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 订单已关闭返回异常信息,更新订单状态为已支付,后续流程不处理,允许用户进行退款操作
|
|
|
+ if (EPaymentStatus.CLOSED == paymentOrder.getStatus() && StringUtils.isNotBlank(paymentOrder.getErrorMsg())) {
|
|
|
+
|
|
|
+ // 支付成功
|
|
|
+ if (PaymentStatus.SUCCESSED == paymentResp.getPaymentStatus()) {
|
|
|
+
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrder order = UserPaymentOrderWrapper.UserPaymentOrder
|
|
|
+ .builder()
|
|
|
+ .transNo(paymentResp.getTransNo())
|
|
|
+ .id(paymentOrder.getId())
|
|
|
+ .status(PAID)
|
|
|
+ .updateTime(DateTime.now().toDate())
|
|
|
+ .build();
|
|
|
+ userPaymentOrderService.updateById(order);
|
|
|
+
|
|
|
+ log.error("executePaymentCallback 关单异常,更新订单状态为已支付, error={}, status={}, paymentResp={}", paymentOrder.getErrorMsg(),
|
|
|
+ paymentOrder.getStatus(), JSON.toJSONString(paymentResp));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 订单状态为待支付、支付中时,更新订单状态;
|
|
|
+ if (EPaymentStatus.WAIT_PAY == paymentOrder.getStatus() || EPaymentStatus.PAYING == paymentOrder.getStatus()) {
|
|
|
+
|
|
|
+ // 支付成功
|
|
|
+ if (PaymentStatus.SUCCESSED == paymentResp.getPaymentStatus()) {
|
|
|
+ // 根据支付回调消息,更新订单状态
|
|
|
+ executePaymentSuccess(UserPaymentOrderWrapper.UserPaymentOrder.from(JSON.toJSONString(paymentOrder)), paymentResp);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 支付失败
|
|
|
+ if (PaymentStatus.FAILED == paymentResp.getPaymentStatus()) {
|
|
|
+
|
|
|
+ // 支付失败,取消订单且更新订单状态
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrder order = UserPaymentOrderWrapper.UserPaymentOrder
|
|
|
+ .builder()
|
|
|
+ .transNo(paymentResp.getTransNo())
|
|
|
+ .id(paymentOrder.getId())
|
|
|
+ .status(EPaymentStatus.FAIL)
|
|
|
+ .updateTime(DateTime.now().toDate())
|
|
|
+ .build();
|
|
|
+ userPaymentOrderService.updateById(order);
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("executePaymentCallback WAIT_PAY, status={}, paymentResp={}", paymentOrder.getStatus(),
|
|
|
+ JSON.toJSONString(paymentResp));
|
|
|
+ }
|
|
|
+
|
|
|
+ }, 60L, TimeUnit.SECONDS);
|
|
|
+
|
|
|
+ log.info("executePaymentCallback paymentResp={}", JSON.toJSONString(paymentResp));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 支付成功处理流程
|
|
|
+ *
|
|
|
+ * @param paymentOrder UserPaymentOrderWrapper.UserPaymentOrder
|
|
|
+ * @param paymentResp PaymentResp
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ @Override
|
|
|
+ public void executePaymentSuccess(UserPaymentOrderWrapper.UserPaymentOrder paymentOrder, PaymentResp paymentResp) {
|
|
|
+
|
|
|
+ // 订单已完成,直接忽略后续流程
|
|
|
+ if (EPaymentStatus.WAIT_PAY != paymentOrder.getStatus() && EPaymentStatus.PAYING != paymentOrder.getStatus()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 支付回调消息,修改订单状态
|
|
|
+ Date date = DateTime.now().toDate();
|
|
|
+
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrder order = UserPaymentOrderWrapper.UserPaymentOrder
|
|
|
+ .builder()
|
|
|
+ .id(paymentOrder.getId())
|
|
|
+ .status(PAID)
|
|
|
+ .payTime(date)
|
|
|
+ .updateTime(date)
|
|
|
+ .build();
|
|
|
+
|
|
|
+ // 三方支付费用
|
|
|
+ if (Objects.nonNull(paymentResp)) {
|
|
|
+
|
|
|
+ // 更新交易时间和服务费用
|
|
|
+ order.payTime(Optional.ofNullable(paymentResp.getTriggerTime()).orElse(order.getPayTime()))
|
|
|
+ .paymentChannelFee(paymentResp.getServiceCharge())
|
|
|
+ .setTransNo(paymentResp.getTransNo());
|
|
|
+ }
|
|
|
+ userPaymentOrderService.updateById(order);
|
|
|
+
|
|
|
+ // 不同订单类型支付成功,后续数据同步流程
|
|
|
+ if (paymentSuccess.containsKey(paymentOrder.getOrderType())) {
|
|
|
+ paymentSuccess.get(paymentOrder.getOrderType()).accept(paymentOrder);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成协议
|
|
|
+ try {
|
|
|
+ sysUserContractRecordService.sign(ContractTemplateTypeEnum.BUY_ORDER, SysUserType.valueOf(paymentOrder.getPaymentClient().name()),
|
|
|
+ sysUserService.findUserById(paymentOrder.getUserId()));
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("executePaymentSuccess 生成协议失败, userId={}, orderNo={}", paymentOrder.getUserId(), paymentOrder.getOrderNo());
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("executePaymentSuccess userId={}, biz={}, orderNo={}", paymentOrder.getUserId(), paymentOrder.getBizId(), paymentOrder.getOrderNo());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 取消订单支付
|
|
|
+ *
|
|
|
+ * @param paymentOrder UserPaymentOrderWrapper.UserPaymentOrder
|
|
|
+ * @param paymentResp PaymentResp
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ @Override
|
|
|
+ public void cancelPayment(UserPaymentOrderWrapper.UserPaymentOrder paymentOrder, PaymentResp paymentResp) {
|
|
|
+
|
|
|
+ // 同步三方订单支付状态
|
|
|
+ String lockName = redisCacheService.getPaymentCacheKey(paymentOrder.getOrderNo());
|
|
|
+
|
|
|
+ DistributedLock.of(redissonClient).runIfLockCanGet(lockName, () -> {
|
|
|
+
|
|
|
+ // 订单支付已完成
|
|
|
+ if (PaymentStatus.SUCCESSED == paymentResp.getPaymentStatus()) {
|
|
|
+
|
|
|
+ // 关闭当前事务,提交订单支付完成流程
|
|
|
+ ThreadPool.getExecutor().submit(() -> {
|
|
|
+ // 完成订单支付流程
|
|
|
+ executePaymentSuccess(paymentOrder, paymentResp);
|
|
|
+ });
|
|
|
+
|
|
|
+ // 提示订单支付已完成
|
|
|
+ throw new BizException("订单支付已完成");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 订单未支付,后续直接更新订单状态为已关闭
|
|
|
+ if (PaymentStatus.PENDDING == paymentResp.getPaymentStatus()) {
|
|
|
+
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrder order = UserPaymentOrderWrapper.UserPaymentOrder
|
|
|
+ .builder()
|
|
|
+ .id(paymentOrder.getId())
|
|
|
+ //.status(EPaymentStatus.CLOSED)
|
|
|
+ //.errorMsg(errorMessage)
|
|
|
+ .updateTime(DateTime.now().toDate())
|
|
|
+ .build();
|
|
|
+
|
|
|
+ if (StringUtils.isBlank(paymentResp.getMsg())) {
|
|
|
+
|
|
|
+ String errorMessage = null;
|
|
|
+ try {
|
|
|
+ // 关闭三方支付订单状态
|
|
|
+ PaymentClose paymentClose = paymentServiceContext.getPaymentService(paymentOrder.getPaymentVender())
|
|
|
+ .close(paymentOrder.getTransNo(), "用户取消支付", paymentOrder.getOrderNo());
|
|
|
+
|
|
|
+ if (Objects.nonNull(paymentClose)) {
|
|
|
+ errorMessage = paymentClose.getMsg();
|
|
|
+
|
|
|
+ // 订单取消,三方返回异常信息
|
|
|
+ if (StringUtils.isNotEmpty(errorMessage)) {
|
|
|
+ log.warn("cancelPayment orderNo={}, ex={}", paymentOrder.getOrderNo(), errorMessage);
|
|
|
+ // 记录异常消息
|
|
|
+ ThreadPool.getExecutor().submit(() -> {
|
|
|
+ order.setErrorMsg(paymentClose.getMsg());
|
|
|
+
|
|
|
+ // 更新订单退款异常信息
|
|
|
+ userPaymentOrderService.updateById(order);
|
|
|
+ });
|
|
|
+ throw new BizException("查询交易中,请耐心等待!");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("cancelPayment orderNo={}", paymentOrder.getOrderNo(), e);
|
|
|
+ throw new BizException("查询交易中,请耐心等待!");
|
|
|
+ }
|
|
|
+
|
|
|
+ order.status(EPaymentStatus.CLOSED).setErrorMsg(errorMessage);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新订单关闭状态
|
|
|
+ userPaymentOrderService.updateById(order);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }, 10L, TimeUnit.SECONDS);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用户下单请求
|
|
|
+ *
|
|
|
+ * @param orderReq UserPaymentOrderWrapper.UserPaymentOrder
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public UserPaymentOrderWrapper.PaymentConfig executeOrderCreate(UserPaymentOrderWrapper.UserPaymentOrder orderReq) {
|
|
|
+
|
|
|
+ try {
|
|
|
+
|
|
|
+ // 商品校验 orderCreate
|
|
|
+
|
|
|
+
|
|
|
+ // 用户下单请求锁
|
|
|
+ String lockName = redisCacheService.getExecuteOrderCacheKey(orderReq.getUserId().toString());
|
|
|
+ // 生成用户支付订单
|
|
|
+ return DistributedLock.of(redissonClient).runIfLockToFunction(lockName, this::executeOrder, orderReq, 10L);
|
|
|
+
|
|
|
+ } catch (BizException e) {
|
|
|
+ log.error("executeOrder BizException, orderReq={}", orderReq.jsonString(), e);
|
|
|
+
|
|
|
+ throw e;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("executeOrder 下单失败, orderReq={}", orderReq.jsonString(), e);
|
|
|
+
|
|
|
+ throw new BizException("下单失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用户下单请求
|
|
|
+ *
|
|
|
+ * @param orderReq UserPaymentOrderWrapper.UserPaymentOrder
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ @Override
|
|
|
+ public UserPaymentOrderWrapper.PaymentConfig executeOrder(UserPaymentOrderWrapper.UserPaymentOrder orderReq) {
|
|
|
+
|
|
|
+ // 填充订单基本信息
|
|
|
+ orderReq.id(IdWorker.getId())
|
|
|
+ .orderNo(IdWorker.getIdStr())
|
|
|
+ .paymentVender(Optional.ofNullable(orderReq.getPaymentType()).orElse(paymentServiceContext.getPaymentService().venderName()))
|
|
|
+ .paymentChannel("")
|
|
|
+ .status(EPaymentStatus.WAIT_PAY)
|
|
|
+ .originalPrice(BigDecimal.ZERO);
|
|
|
+
|
|
|
+ // 购买商品信息
|
|
|
+ for (UserOrderDetail item : orderReq.getGoodsInfos()) {
|
|
|
+
|
|
|
+ item.setOrderNo(orderReq.getOrderNo());
|
|
|
+ item.setSubOrderNo(IdWorker.getIdStr());
|
|
|
+
|
|
|
+ // 商品基本信息
|
|
|
+ if (orderGoodsCreate.containsKey(item.getGoodType())) {
|
|
|
+ // 填充商品基础信息,校验参数合法以性
|
|
|
+ orderGoodsCreate.get(item.getGoodType()).accept(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算原始价格
|
|
|
+ BigDecimal originalPrice = orderReq.getGoodsInfos().stream()
|
|
|
+ .map(UserOrderDetail::getOriginalPrice)
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+
|
|
|
+ // 支付金额
|
|
|
+ BigDecimal currentPrice = orderReq.getGoodsInfos().stream()
|
|
|
+ .map(UserOrderDetail::getExpectPrice)
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+
|
|
|
+ // 实际支付金额
|
|
|
+ BigDecimal cashAmount = orderReq.getGoodsInfos().stream()
|
|
|
+ .map(UserOrderDetail::getActualPrice)
|
|
|
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
+
|
|
|
+ // 根据优惠券计算实际优惠金额
|
|
|
+
|
|
|
+ orderReq.originalPrice(originalPrice.setScale(2, RoundingMode.HALF_UP))
|
|
|
+ .currentPrice(currentPrice.setScale(2, RoundingMode.HALF_UP))
|
|
|
+ .paymentCouponAmount(BigDecimal.ZERO)
|
|
|
+ .actualPrice(cashAmount.setScale(2, RoundingMode.HALF_UP));
|
|
|
+
|
|
|
+ // 验证支付金额是否匹配
|
|
|
+ if (orderReq.getActualPrice().setScale(2, RoundingMode.HALF_UP)
|
|
|
+ .compareTo(orderReq.getPaymentCashAmount().setScale(2, RoundingMode.HALF_UP)) != 0) {
|
|
|
+ log.info("executeOrder actualPrice={}", orderReq.getActualPrice());
|
|
|
+ throw new BizException("支付金额不匹配");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 订单入库前处理流程
|
|
|
+ if (orderSuccessBefore.containsKey(orderReq.getOrderType())) {
|
|
|
+ orderSuccessBefore.get(orderReq.getOrderType()).accept(orderReq);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证成功后,订单入库
|
|
|
+
|
|
|
+ // 查询支付三方配置
|
|
|
+ BasePaymentService paymentService = paymentServiceContext.getPaymentService(orderReq.getPaymentVender());
|
|
|
+ if (Objects.nonNull(paymentService)) {
|
|
|
+ // 设置支付三方AppId
|
|
|
+ orderReq.setPaymentAppId(paymentService.getPaymentConfig().getAppId());
|
|
|
+ }
|
|
|
+ userPaymentOrderService.save(orderReq);
|
|
|
+
|
|
|
+ userOrderDetailService.saveBatch(orderReq.getGoodsInfos());
|
|
|
+
|
|
|
+ // 更新用户订单优惠券使用状态
|
|
|
+
|
|
|
+ String subject = orderReq.getGoodsInfos().stream()
|
|
|
+ .map(UserOrderDetail::getGoodName).collect(Collectors.joining(","));
|
|
|
+ // 返回订单支付配置参数
|
|
|
+ UserPaymentOrderWrapper.PaymentOrderReqConfig reqConfig = UserPaymentOrderWrapper.PaymentOrderReqConfig
|
|
|
+ .builder()
|
|
|
+ .merOrderNo(orderReq.getOrderNo())
|
|
|
+ .subject(subject)
|
|
|
+ .body(subject)
|
|
|
+ .price(orderReq.getPaymentCashAmount().setScale(2, RoundingMode.HALF_UP))
|
|
|
+ .expirationTime(DateTime.now().plusMinutes(30).toDate())
|
|
|
+ .wxAppId(paymentServiceContext.getPaymentService(orderReq.getPaymentVender()).getPaymentConfig().getWxAppId())
|
|
|
+ .userId(String.valueOf(orderReq.getUserId()))
|
|
|
+ .build();
|
|
|
+
|
|
|
+ // 缓存用户下单配置
|
|
|
+ redisCacheService.saveUserOrderConfig(String.valueOf(orderReq.getUserId()), orderReq.getOrderType().getCode(), reqConfig.jsonString());
|
|
|
+
|
|
|
+ // 若订单无需支付,直接完成订单,VIP购买
|
|
|
+ /*if (BigDecimal.ZERO.compareTo(orderReq.getPaymentCashAmount()) == 0 && EPaymentType.VIP == orderReq.getOrderType()) {
|
|
|
+ executePaymentSuccess(orderReq, null);
|
|
|
+ }*/
|
|
|
+
|
|
|
+ return UserPaymentOrderWrapper.PaymentConfig.builder()
|
|
|
+ .orderNo(orderReq.getOrderNo())
|
|
|
+ .paymentConfig(reqConfig)
|
|
|
+ .paymentType(paymentServiceContext.getPaymentService(orderReq.getPaymentVender()).venderName())
|
|
|
+ .build();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用户支付请求
|
|
|
+ *
|
|
|
+ * @param reqConfig UserPaymentOrderWrapper.PaymentOrderReqConfig
|
|
|
+ * @return UserPaymentOrderWrapper.PaymentReq
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ @Override
|
|
|
+ public UserPaymentOrderWrapper.PaymentReq executePayment(JwtUserInfo<?> userInfo,
|
|
|
+ UserPaymentOrderWrapper.PaymentOrderReqConfig reqConfig) {
|
|
|
+
|
|
|
+ // 设置支付用户
|
|
|
+ reqConfig.setUserId(userInfo.getUserId());
|
|
|
+
|
|
|
+ // 用户下单请求锁
|
|
|
+ String lockName = redisCacheService.getPaymentCacheKey(userInfo.getUserId());
|
|
|
+
|
|
|
+ // 分布式锁,用户支付请求
|
|
|
+ return DistributedLock.of(redissonClient).runIfLockToFunction(lockName, this::executePayment, reqConfig, 10L);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新用户支付订单状态
|
|
|
+ * @param userId 支付用户Id
|
|
|
+ * @param userPaymentOrder UserPaymentOrder
|
|
|
+ * @param paymentResp PaymentResp
|
|
|
+ */
|
|
|
+ public void updateUserPaymentOrderStatus(String userId, UserPaymentOrderWrapper.UserPaymentOrder userPaymentOrder, PaymentResp paymentResp) {
|
|
|
+
|
|
|
+ // 若三方支付已完,本地订单状态未同步
|
|
|
+ if (PaymentStatus.SUCCESSED == paymentResp.getPaymentStatus() && PAID != userPaymentOrder.getStatus()) {
|
|
|
+
|
|
|
+ if (EPaymentStatus.WAIT_PAY == userPaymentOrder.getStatus() || EPaymentStatus.PAYING == userPaymentOrder.getStatus()) {
|
|
|
+
|
|
|
+ // 订单完成后续流程
|
|
|
+ SpringContextHolder.getBean(UserPaymentCoreService.class)
|
|
|
+ .executePaymentSuccess(UserPaymentOrderWrapper.UserPaymentOrder.from(JSON.toJSONString(userPaymentOrder)), paymentResp);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 删除支付配置
|
|
|
+ redisCacheService.deleteUserPaymentConfig(userId, userPaymentOrder.getOrderType().getCode());
|
|
|
+
|
|
|
+ // 删除下单配置
|
|
|
+ redisCacheService.deleteUserOrderConfig(userId, userPaymentOrder.getOrderType().getCode());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 三方支付失败
|
|
|
+ if (PaymentStatus.FAILED == paymentResp.getPaymentStatus() && EPaymentStatus.FAIL != userPaymentOrder.getStatus()) {
|
|
|
+
|
|
|
+ // 更新订单状态
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrder update = UserPaymentOrderWrapper.UserPaymentOrder
|
|
|
+ .builder()
|
|
|
+ .id(userPaymentOrder.getId())
|
|
|
+ .status(EPaymentStatus.FAIL)
|
|
|
+ .build();
|
|
|
+ userPaymentOrderService.updateById(update);
|
|
|
+
|
|
|
+ // 订单关闭流程
|
|
|
+ SpringContextHolder.getBean(UserPaymentCoreService.class)
|
|
|
+ .cancelPayment(UserPaymentOrderWrapper.UserPaymentOrder.from(JSON.toJSONString(userPaymentOrder)), paymentResp);
|
|
|
+
|
|
|
+ // 删除本地缓存
|
|
|
+ redisCacheService.deleteUserPaymentConfig(userId, userPaymentOrder.getOrderType().getCode());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用户支付请求
|
|
|
+ *
|
|
|
+ * @param reqConfig UserPaymentOrderWrapper.PaymentOrderReqConfig
|
|
|
+ * @return UserPaymentOrderWrapper.PaymentReq
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ @Override
|
|
|
+ public UserPaymentOrderWrapper.PaymentReq executePayment(UserPaymentOrderWrapper.PaymentOrderReqConfig reqConfig) {
|
|
|
+
|
|
|
+ // 查询订单状态
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrder userPaymentOrder = userPaymentOrderService.getUserPaymentOrderByUserId(Long.parseLong(reqConfig.getUserId()),
|
|
|
+ reqConfig.getMerOrderNo());
|
|
|
+ if (Objects.isNull(userPaymentOrder)) {
|
|
|
+ throw new BizException("订单不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+// 订单支付已完成
|
|
|
+ switch (userPaymentOrder.getStatus()) {
|
|
|
+ case PAID:
|
|
|
+ throw new BizException("订单已支付");
|
|
|
+ case CLOSED:
|
|
|
+ throw new BizException("订单已关闭");
|
|
|
+ case TIMEOUT:
|
|
|
+ throw new BizException("订单已超时");
|
|
|
+ case FAIL:
|
|
|
+ throw new BizException("订单支付失败");
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+// 订单支付状态异常
|
|
|
+ if (EPaymentStatus.WAIT_PAY != userPaymentOrder.getStatus() && EPaymentStatus.PAYING != userPaymentOrder.getStatus()) {
|
|
|
+ throw new BizException("订单支付状态异常");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 微信支付,需要校验code值是否为空
|
|
|
+ if (EPaymentChannel.WX_PUB.getCode().equals(reqConfig.getPaymentChannel())
|
|
|
+ && StringUtils.isBlank(reqConfig.getCode())) {
|
|
|
+ throw new BizException("微信支付code值为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成订单支付参数,三方返返回异常,直接关闭订单
|
|
|
+ try {
|
|
|
+
|
|
|
+ // 已产生三方交易流水号,先判断三方订单支付状态若在支付中,则重复返回相同支付配置参数;避免用户重复支付
|
|
|
+ if (StringUtils.isNotBlank(userPaymentOrder.getTransNo())) {
|
|
|
+
|
|
|
+ // 获取缓存配置信息
|
|
|
+ String paymentConfig = redisCacheService.getUserPaymentConfig(reqConfig.getUserId(), userPaymentOrder.getOrderType().getCode());
|
|
|
+ if (StringUtils.isBlank(paymentConfig)) {
|
|
|
+
|
|
|
+ // 支付配置已失效,待支付订单状态调整为已关闭,提醒用户重新购买
|
|
|
+ PaymentClose paymentClose = paymentServiceContext.getPaymentService(userPaymentOrder.getPaymentVender())
|
|
|
+ .close(userPaymentOrder.getTransNo(), "订单支付已超时", userPaymentOrder.getOrderNo());
|
|
|
+
|
|
|
+ // 更新订单状态为已超时
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrder update = UserPaymentOrderWrapper.UserPaymentOrder
|
|
|
+ .builder()
|
|
|
+ .id(userPaymentOrder.getId())
|
|
|
+ .status(EPaymentStatus.CLOSED)
|
|
|
+ .errorMsg(paymentClose.getMsg())
|
|
|
+ .build();
|
|
|
+ userPaymentOrderService.updateById(update);
|
|
|
+
|
|
|
+ throw BizException.from("支付超时");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 返回支付配置信息
|
|
|
+ return UserPaymentOrderWrapper.PaymentReq.from(paymentConfig);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询用户下单配置
|
|
|
+ String ret = redisCacheService.getUserOrderConfig(reqConfig.getUserId(), userPaymentOrder.getOrderType().getCode());
|
|
|
+ if (StringUtils.isEmpty(ret)) {
|
|
|
+ throw new BizException("支付超时");
|
|
|
+ }
|
|
|
+ UserPaymentOrderWrapper.PaymentOrderReqConfig config = UserPaymentOrderWrapper.PaymentOrderReqConfig
|
|
|
+ .from(ret)
|
|
|
+ .paymentChannel(reqConfig.getPaymentChannel())
|
|
|
+ .merOrderNo(reqConfig.getMerOrderNo())
|
|
|
+ .code(reqConfig.getCode());
|
|
|
+
|
|
|
+ // 重新格式化设置价格
|
|
|
+ config.setPrice(config.getPrice().setScale(2, RoundingMode.HALF_UP));
|
|
|
+ log.info("executePayment reqConfig={}", config.jsonString());
|
|
|
+
|
|
|
+ PaymentOrder paymentOrder = JSON.parseObject(config.jsonString(), PaymentOrder.class);
|
|
|
+ PaymentReq paymentReq = paymentServiceContext.getPaymentService(userPaymentOrder.getPaymentVender()).pay(paymentOrder);
|
|
|
+ if (Objects.isNull(paymentReq)) {
|
|
|
+ throw BizException.from("生成支付参数异常");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新订单流水号
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrder update = UserPaymentOrderWrapper.UserPaymentOrder
|
|
|
+ .builder()
|
|
|
+ .id(userPaymentOrder.getId())
|
|
|
+ .transNo(paymentReq.getTransNo())
|
|
|
+ .paymentChannel(reqConfig.getPaymentChannel())
|
|
|
+ .transPayTime(DateTime.now().toDate())
|
|
|
+ .status(EPaymentStatus.PAYING)
|
|
|
+ .build();
|
|
|
+ userPaymentOrderService.updateById(update);
|
|
|
+
|
|
|
+ // 缓存用户支付配置信息
|
|
|
+ redisCacheService.saveUserPaymentConfig(reqConfig.getUserId(), userPaymentOrder.getOrderType().getCode(),
|
|
|
+ JSON.toJSONString(paymentReq));
|
|
|
+
|
|
|
+ return UserPaymentOrderWrapper.PaymentReq.from(JSON.toJSONString(paymentReq));
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("executePayment reqConfig={}", reqConfig.jsonString(), e);
|
|
|
+
|
|
|
+ // 拉起三方支付失败,主动调整订单状态为已关闭
|
|
|
+ userPaymentOrderService.closeUserPaymentOrder(userPaymentOrder.getId());
|
|
|
+
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 用户取消支付
|
|
|
+ *
|
|
|
+ * @param userInfo JwtUserInfo
|
|
|
+ * @param orderNo 订单编号
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ @Override
|
|
|
+ public void cancelPayment(JwtUserInfo<?> userInfo, String orderNo) {
|
|
|
+
|
|
|
+ // 查询订单信息,支付状态
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrder paymentOrder = userPaymentOrderService.getUserPaymentOrderByUserId(Long.parseLong(userInfo.getUserId()), orderNo);
|
|
|
+ if (Objects.isNull(paymentOrder)) {
|
|
|
+ throw new BizException("订单不存在");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 订单状态处理
|
|
|
+ switch (paymentOrder.getStatus()) {
|
|
|
+ case PAID:
|
|
|
+ // 订单支付已完成不可取消,走申请退款流程
|
|
|
+ throw new BizException("订单已支付");
|
|
|
+ case REFUNDED:
|
|
|
+ // 订单已取消
|
|
|
+ throw new BizException("订单已取消");
|
|
|
+ case CLOSED:
|
|
|
+ throw new BizException("订单已关闭");
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 任务执行锁
|
|
|
+ String lockName = redisCacheService.getPaymentCacheKey(orderNo);
|
|
|
+ DistributedLock.of(redissonClient).runIfLockCanGet(lockName, () -> {
|
|
|
+
|
|
|
+ // 下单未拉起三方支付,直接关闭
|
|
|
+ if (StringUtils.isEmpty(paymentOrder.getTransNo())) {
|
|
|
+
|
|
|
+ // 直接关闭订单
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrder order = UserPaymentOrderWrapper.UserPaymentOrder
|
|
|
+ .builder()
|
|
|
+ .id(paymentOrder.getId())
|
|
|
+ .status(EPaymentStatus.CLOSED)
|
|
|
+ .updateTime(DateTime.now().toDate())
|
|
|
+ .build();
|
|
|
+ userPaymentOrderService.updateById(order);
|
|
|
+
|
|
|
+
|
|
|
+ } else {
|
|
|
+ // 获取三方支付订单信息,根据订单状处理
|
|
|
+ PaymentResp paymentResp = paymentServiceContext.getPaymentService(paymentOrder.getPaymentVender())
|
|
|
+ .query(paymentOrder.getTransNo(), paymentOrder.getOrderNo());
|
|
|
+ if (Objects.isNull(paymentResp)) {
|
|
|
+ throw new BizException("订单支付状态获取异常,稍后请重试");
|
|
|
+ }
|
|
|
+ // 支付订单信息
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrder order = UserPaymentOrderWrapper.UserPaymentOrder.from(JSON.toJSONString(paymentOrder));
|
|
|
+
|
|
|
+ // 订单取消执行流程
|
|
|
+ cancelPayment(order, paymentResp);
|
|
|
+ }
|
|
|
+
|
|
|
+ }, 10L, TimeUnit.SECONDS);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 扫描支付超时定时
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void scanPaymentTimeoutOrderRecord() {
|
|
|
+
|
|
|
+ // 查询半小时前创建的并且还未完成的订单 WAIT_PAY 待支付订单 PAYING 支付中订单
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrderQuery waitPayQuery = UserPaymentOrderWrapper.UserPaymentOrderQuery
|
|
|
+ .builder()
|
|
|
+ .paymentStatus(Lists.newArrayList(OrderStatusEnum.parse(EPaymentStatus.WAIT_PAY)))
|
|
|
+ .endTime(DateTime.now().minusMinutes(30).toDate())
|
|
|
+ .build();
|
|
|
+
|
|
|
+ IPage<UserPaymentOrderWrapper.UserPaymentOrder> page = QueryInfo.getPage(1, 10);
|
|
|
+ // 查询待支付订单数
|
|
|
+ userPaymentOrderService.selectPage(page, waitPayQuery);
|
|
|
+
|
|
|
+ // 更新订单状态
|
|
|
+ scheduleUpdatePaymentOrderStatus(page, waitPayQuery, EPaymentStatus.WAIT_PAY);
|
|
|
+
|
|
|
+ // 查询支付中订单,超过2个小时且还未完成自动关闭
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrderQuery payingQuery = UserPaymentOrderWrapper.UserPaymentOrderQuery
|
|
|
+ .builder()
|
|
|
+ .paymentStatus(Lists.newArrayList(OrderStatusEnum.parse(EPaymentStatus.PAYING)))
|
|
|
+ .endTime(DateTime.now().plusMinutes(120).toDate())
|
|
|
+ .build();
|
|
|
+ // 查询支付中订单数
|
|
|
+ userPaymentOrderService.selectPage(page, payingQuery);
|
|
|
+
|
|
|
+ // 更新订单状态
|
|
|
+ scheduleUpdatePaymentOrderStatus(page, payingQuery, EPaymentStatus.PAYING);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 定时更新订单状态
|
|
|
+ * @param page IPage<UserPaymentOrderWrapper.UserPaymentOrder>
|
|
|
+ * @param orderQuery UserPaymentOrderWrapper.UserPaymentOrderQuery
|
|
|
+ * @param paymentStatus 订单状态
|
|
|
+ */
|
|
|
+ public void scheduleUpdatePaymentOrderStatus(IPage<UserPaymentOrderWrapper.UserPaymentOrder> page,
|
|
|
+ UserPaymentOrderWrapper.UserPaymentOrderQuery orderQuery,
|
|
|
+ EPaymentStatus paymentStatus) {
|
|
|
+
|
|
|
+ // 分页数限制
|
|
|
+ int limit = 500;
|
|
|
+ // 计算总页数
|
|
|
+ int totalPages = (int) (page.getTotal() - 1) / limit + 1;
|
|
|
+
|
|
|
+ List<Integer> pageNums = IntStream.iterate(1, i -> i + 1).limit(totalPages).boxed().collect(Collectors.toList());
|
|
|
+
|
|
|
+ IPage<UserPaymentOrderWrapper.UserPaymentOrder> pages;
|
|
|
+ String lockName;
|
|
|
+ for (Integer pageNum : pageNums) {
|
|
|
+
|
|
|
+ pages = userPaymentOrderService.selectPage(QueryInfo.getPage(pageNum, limit), orderQuery);
|
|
|
+ if (CollectionUtils.isNotEmpty(pages.getRecords())) {
|
|
|
+
|
|
|
+ for (UserPaymentOrderWrapper.UserPaymentOrder item : pages.getRecords()) {
|
|
|
+
|
|
|
+ lockName = redisCacheService.getPaymentCacheKey(item.getOrderNo());
|
|
|
+ // 并发执行锁
|
|
|
+ DistributedLock.of(redissonClient).runIfLockCanGet(lockName, () -> {
|
|
|
+
|
|
|
+ // 订单用户信息
|
|
|
+ JwtUserInfo<Object> userInfo = JwtUserInfo.builder()
|
|
|
+ .userId(String.valueOf(item.getUserId()))
|
|
|
+ .clientType(item.getPaymentClient().getCode())
|
|
|
+ .build();
|
|
|
+
|
|
|
+ // 用户下单超进关闭
|
|
|
+ if (EPaymentStatus.WAIT_PAY == paymentStatus
|
|
|
+ || new DateTime(item.getCreateTime()).plusMinutes(118).isBeforeNow()) {
|
|
|
+
|
|
|
+ try {
|
|
|
+ log.info("scanPaymentTimeoutOrderRecord WAIT_PAY, userId={}, orderNo={}", item.getUserId(), item.getOrderNo());
|
|
|
+ // 关闭待支付、超时订单
|
|
|
+ SpringContextHolder.getBean(UserPaymentCoreService.class).cancelPayment(userInfo, item.getOrderNo());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("scanPaymentTimeoutOrderRecord WAIT_PAY, userId={}, orderNo={}", item.getUserId(), item.getOrderNo(), e);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // 支付时间未超时,同步订单状态
|
|
|
+ if (EPaymentStatus.PAYING == paymentStatus
|
|
|
+ && new DateTime(item.getCreateTime()).plusMinutes(120).isAfterNow()) {
|
|
|
+
|
|
|
+ // 支付同小消息
|
|
|
+ PaymentResp paymentResp = paymentServiceContext.getPaymentService(item.getPaymentVender()).query(item.getTransNo(), item.getOrderNo());
|
|
|
+ if (Objects.nonNull(paymentResp)) {
|
|
|
+
|
|
|
+ try {
|
|
|
+
|
|
|
+ log.info("scanPaymentTimeoutOrderRecord PAYMENT_SYNC, vendor={}, paymentResp={}", item.getPaymentVender(),
|
|
|
+ JSON.toJSONString(paymentResp));
|
|
|
+ // 支付成功
|
|
|
+ if (PaymentStatus.SUCCESSED == paymentResp.getPaymentStatus()) {
|
|
|
+ // 根据支付回调消息,更新订单状态
|
|
|
+ SpringContextHolder.getBean(UserPaymentCoreService.class)
|
|
|
+ .executePaymentSuccess(UserPaymentOrderWrapper.UserPaymentOrder.from(JSON.toJSONString(item)), paymentResp);
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("scanPaymentTimeoutOrderRecord PAYMENT_SYNC, userId={}, orderNo={}", item.getUserId(), item.getOrderNo(), e);
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }, 60L, TimeUnit.SECONDS);
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //log.info("scheduleUpdatePaymentOrderStatus PAYMENT_FINISH ------> ");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+}
|