liujie
2026-08-21 77649c76ec1c3afcb93e9963243d637665b3a307
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package com.tourguide.modules.order.service;
 
import com.tourguide.common.exception.BusinessException;
import com.tourguide.common.huifu.RequestSeqUtil;
import com.tourguide.modules.order.domain.OrderStatus;
import com.tourguide.modules.order.domain.PaymentMethod;
import com.tourguide.modules.order.entity.TourOrder;
import com.tourguide.modules.order.entity.TourOrderPaymentAttempt;
import com.tourguide.modules.order.mapper.TourOrderMapper;
import com.tourguide.modules.order.mapper.TourOrderPaymentAttemptMapper;
import com.tourguide.modules.order.payment.HuifuPaymentNotice;
import com.tourguide.modules.order.payment.OrderPaymentPreparation;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
 
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
 
@Service
public class TourOrderPaymentService {
    private static final DateTimeFormatter REQUEST_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyyMMdd");
    private static final String ATTEMPT_CREATED = "CREATED";
    private static final String ATTEMPT_SUCCEEDED = "SUCCEEDED";
 
    private final TourOrderMapper orderMapper;
    private final TourOrderPaymentAttemptMapper paymentAttemptMapper;
    private final GuideOrderMatchingService matchingService;
 
    public TourOrderPaymentService(TourOrderMapper orderMapper,
                                   TourOrderPaymentAttemptMapper paymentAttemptMapper,
                                   GuideOrderMatchingService matchingService) {
        this.orderMapper = orderMapper;
        this.paymentAttemptMapper = paymentAttemptMapper;
        this.matchingService = matchingService;
    }
 
    @Transactional
    public OrderPaymentPreparation prepare(Long visitorId, Long orderId) {
        TourOrder order = orderMapper.selectForUpdate(orderId);
        if (order == null || visitorId == null || !visitorId.equals(order.getVisitorId())) {
            throw new BusinessException(404, "订单不存在");
        }
        if (!OrderStatus.PENDING_PAYMENT.matches(order.getStatus())) {
            throw new BusinessException(409, "当前订单状态不支持支付");
        }
 
        LocalDateTime now = LocalDateTime.now();
        String reqDate = now.format(REQUEST_DATE_FORMAT);
        String reqSeqId = RequestSeqUtil.generateForBusiness("PY");
        TourOrderPaymentAttempt attempt = new TourOrderPaymentAttempt();
        attempt.setOrderId(order.getId());
        attempt.setReqSeqId(reqSeqId);
        attempt.setReqDate(reqDate);
        attempt.setAmount(order.getAmount());
        attempt.setStatus(ATTEMPT_CREATED);
        paymentAttemptMapper.insert(attempt);
 
        order.setPaymentMethod(PaymentMethod.WECHAT.getCode());
        order.setPaymentOrderNo(reqSeqId);
        orderMapper.updateById(order);
 
        String goodsDescription = StringUtils.hasText(order.getPackageName())
                ? order.getScenicName() + "-" + order.getPackageName()
                : order.getScenicName() + "-讲解服务";
        return new OrderPaymentPreparation(order.getId(), reqSeqId, reqDate,
                order.getAmount(), goodsDescription);
    }
 
    @Transactional
    public boolean confirm(HuifuPaymentNotice notice) {
        TourOrderPaymentAttempt attempt = paymentAttemptMapper.selectByReqSeqIdForUpdate(notice.getReqSeqId());
        if (attempt == null) throw new BusinessException(404, "支付流水不存在");
        TourOrder order = orderMapper.selectForUpdate(attempt.getOrderId());
        if (order == null) throw new BusinessException(404, "支付订单不存在");
        validateAmount(attempt.getAmount(), notice.getAmount());
        validateAmount(order.getAmount(), notice.getAmount());
        if (ATTEMPT_SUCCEEDED.equals(attempt.getStatus())) {
            return false;
        }
 
        LocalDateTime paidAt = notice.getPaidAt() == null ? LocalDateTime.now() : notice.getPaidAt();
        attempt.setStatus(ATTEMPT_SUCCEEDED);
        attempt.setProviderTransactionId(notice.getProviderTransactionId());
        attempt.setPaidAt(paidAt);
        paymentAttemptMapper.updateById(attempt);
 
        if (order.getPaymentTime() != null && !OrderStatus.PENDING_PAYMENT.matches(order.getStatus())) {
            return false;
        }
        if (!OrderStatus.PENDING_PAYMENT.matches(order.getStatus())) {
            throw new BusinessException(409, "订单状态不允许确认支付");
        }
        order.setPaymentMethod(PaymentMethod.WECHAT.getCode());
        order.setPaymentOrderNo(notice.getReqSeqId());
        order.setPaymentAmount(notice.getAmount());
        order.setPaymentTime(paidAt);
        order.setStatus(OrderStatus.PENDING_ACCEPT.getCode());
        orderMapper.updateById(order);
        matchingService.match(order.getId());
        return true;
    }
 
    private void validateAmount(BigDecimal expectedAmount, BigDecimal paidAmount) {
        if (expectedAmount == null || paidAmount == null || expectedAmount.compareTo(paidAmount) != 0) {
            throw new BusinessException(409, "支付金额与订单金额不一致");
        }
    }
}