wlzboy
2025-12-02 d294abb765e4ed349907c92ce313689c6299ba7d
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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
package com.ruoyi.payment.application.service;
 
import com.alibaba.fastjson.JSON;
import com.ruoyi.payment.domain.enums.ClientType;
import com.ruoyi.payment.domain.enums.OrderStatus;
import com.ruoyi.payment.domain.enums.PayChannel;
import com.ruoyi.payment.domain.enums.TransactionStatus;
import com.ruoyi.payment.domain.model.PaymentOrder;
import com.ruoyi.payment.domain.model.PaymentTransaction;
import com.ruoyi.payment.infrastructure.channel.alipay.AlipayF2FClient;
import com.ruoyi.payment.infrastructure.channel.alipay.AlipayThirdPartyClient;
import com.ruoyi.payment.infrastructure.channel.wechat.WxPayV2Client;
import com.ruoyi.payment.infrastructure.config.AlipayConfig;
import com.ruoyi.payment.infrastructure.config.QrCodeConfig;
import com.ruoyi.payment.infrastructure.persistence.mapper.PaymentOrderMapper;
import com.ruoyi.payment.infrastructure.persistence.mapper.PaymentTransactionMapper;
import com.ruoyi.payment.infrastructure.util.QrCodeUtil;
import com.ruoyi.payment.infrastructure.util.SnowflakeIdGenerator;
import com.ruoyi.payment.interfaces.dto.PaymentRequest;
import com.ruoyi.payment.interfaces.dto.PaymentResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
 
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
 
/**
 * 支付应用服务
 * 
 * @author ruoyi
 */
@Slf4j
@Service
public class PaymentService {
 
    @Autowired
    private PaymentOrderMapper paymentOrderMapper;
 
    @Autowired
    private PaymentTransactionMapper paymentTransactionMapper;
 
    @Autowired
    private QrCodeUtil qrCodeUtil;
 
    @Autowired
    private QrCodeConfig qrCodeConfig;
 
    @Autowired
    private WxPayV2Client wxPayV2Client;
 
    @Autowired
    private AlipayF2FClient alipayF2FClient;
 
    @Autowired
    private AlipayThirdPartyClient alipayThirdPartyClient;
 
    @Autowired
    private AlipayConfig alipayConfig;
 
    /**
     * 发起微信Native支付
     */
    @Transactional(rollbackFor = Exception.class)
    public PaymentResponse createWechatNativePayment(PaymentRequest request) {
        log.info("发起微信Native支付,请求参数: {}", JSON.toJSONString(request));
 
        // 1. 查询或创建订单
        PaymentOrder order = getOrCreateOrder(request, PayChannel.WECHAT.getCode());
 
        // 2. 检查是否存在待支付交易(同一订单只允许一笔进行中)
        PaymentTransaction existingTransaction = paymentTransactionMapper.selectPendingByOrderId(order.getId());
        if (existingTransaction != null) {
            log.info("订单{}已存在待支付交易{},直接返回", order.getId(), existingTransaction.getId());
            return buildPaymentResponse(order, existingTransaction);
        }
 
        // 3. 调用微信下单接口(这里先模拟)
        String codeUrl = callWechatUnifiedOrder(order);
 
        // 4. 生成二维码Base64
        String qrBase64 = qrCodeUtil.generateQrCodeBase64(codeUrl, qrCodeConfig.getSize());
 
        // 5. 创建交易记录
        PaymentTransaction transaction = new PaymentTransaction();
        transaction.setId(SnowflakeIdGenerator.generateId());
        transaction.setOrderId(order.getId());
        transaction.setChannel(PayChannel.WECHAT.getCode());
        transaction.setClientType(ClientType.NATIVE.getCode());
        transaction.setStatus(TransactionStatus.PENDING.getCode());
        transaction.setCodeOrQr(codeUrl);
        transaction.setQrBase64(qrBase64);
        transaction.setCreatedAt(LocalDateTime.now());
 
        // 构造请求参数快照
        Map<String, Object> requestParams = new HashMap<>();
        requestParams.put("bizOrderId", request.getBizOrderId());
        requestParams.put("amount", request.getAmount());
        requestParams.put("subject", request.getSubject());
        transaction.setRequestParams(JSON.toJSONString(requestParams));
 
        paymentTransactionMapper.insert(transaction);
 
        // 6. 更新订单最新交易ID
        order.setLatestTransactionId(transaction.getId());
        paymentOrderMapper.update(order);
 
        log.info("微信Native支付创建成功,订单ID: {}, 交易ID: {}", order.getId(), transaction.getId());
 
        return buildPaymentResponse(order, transaction);
    }
 
    /**
     * 发起支付宝当面付
     */
    @Transactional(rollbackFor = Exception.class)
    public PaymentResponse createAlipayPrecreate(PaymentRequest request) {
        log.info("发起支付宝当面付,请求参数: {}", JSON.toJSONString(request));
 
        // 1. 查询或创建订单
        PaymentOrder order = getOrCreateOrder(request, PayChannel.ALIPAY.getCode());
 
        // 2. 检查是否存在待支付交易
        PaymentTransaction existingTransaction = paymentTransactionMapper.selectPendingByOrderId(order.getId());
        if (existingTransaction != null) {
            log.info("订单{}已存在待支付交易{},直接返回", order.getId(), existingTransaction.getId());
            return buildPaymentResponse(order, existingTransaction);
        }
 
        // 3. 调用支付宝下单接口(这里先模拟)
        String qrCode = callAlipayPrecreate(order);
 
        // 4. 生成二维码Base64
        String qrBase64 = qrCodeUtil.generateQrCodeBase64(qrCode, qrCodeConfig.getSize());
 
        // 5. 创建交易记录
        PaymentTransaction transaction = new PaymentTransaction();
        transaction.setId(SnowflakeIdGenerator.generateId());
        transaction.setOrderId(order.getId());
        transaction.setChannel(PayChannel.ALIPAY.getCode());
        transaction.setClientType(ClientType.ALIPAY_PRECREATE.getCode());
        transaction.setStatus(TransactionStatus.PENDING.getCode());
        transaction.setCodeOrQr(qrCode);
        transaction.setQrBase64(qrBase64);
        transaction.setCreatedAt(LocalDateTime.now());
 
        Map<String, Object> requestParams = new HashMap<>();
        requestParams.put("bizOrderId", request.getBizOrderId());
        requestParams.put("amount", request.getAmount());
        requestParams.put("subject", request.getSubject());
        transaction.setRequestParams(JSON.toJSONString(requestParams));
 
        paymentTransactionMapper.insert(transaction);
 
        // 6. 更新订单
        order.setLatestTransactionId(transaction.getId());
        paymentOrderMapper.update(order);
 
        log.info("支付宝当面付创建成功,订单ID: {}, 交易ID: {}", order.getId(), transaction.getId());
 
        return buildPaymentResponse(order, transaction);
    }
 
    /**
     * 发起支付宝当面付(第三方接口)
     */
    @Transactional(rollbackFor = Exception.class)
    public PaymentResponse createAlipayThirdPartyPrecreate(PaymentRequest request) {
        log.info("发起支付宝当面付(第三方接口),请求参数: {}", JSON.toJSONString(request));
 
        // 1. 查询或创建订单
        PaymentOrder order = getOrCreateOrder(request, PayChannel.ALIPAY.getCode());
 
        // 2. 检查是否存在待支付交易
        PaymentTransaction existingTransaction = paymentTransactionMapper.selectPendingByOrderId(order.getId());
        if (existingTransaction != null) {
            log.info("订单{}已存在待支付交易{},直接返回", order.getId(), existingTransaction.getId());
            return buildPaymentResponse(order, existingTransaction);
        }
 
        // 3. 调用第三方支付宝接口生成支付URL
        String payUrl = callAlipayThirdPartyPrecreate(order, request);
 
        // 4. 生成二维码Base64
        String qrBase64 = qrCodeUtil.generateQrCodeBase64(payUrl, qrCodeConfig.getSize());
 
        // 5. 创建交易记录
        PaymentTransaction transaction = new PaymentTransaction();
        transaction.setId(SnowflakeIdGenerator.generateId());
        transaction.setOrderId(order.getId());
        transaction.setChannel(PayChannel.ALIPAY.getCode());
        transaction.setClientType(ClientType.ALIPAY_PRECREATE.getCode());
        transaction.setStatus(TransactionStatus.PENDING.getCode());
        transaction.setCodeOrQr(payUrl);
        transaction.setQrBase64(qrBase64);
        transaction.setCreatedAt(LocalDateTime.now());
 
        Map<String, Object> requestParams = new HashMap<>();
        requestParams.put("bizOrderId", request.getBizOrderId());
        requestParams.put("amount", request.getAmount());
        requestParams.put("subject", request.getSubject());
        requestParams.put("thirdParty", true);
        transaction.setRequestParams(JSON.toJSONString(requestParams));
 
        paymentTransactionMapper.insert(transaction);
 
        // 6. 更新订单
        order.setLatestTransactionId(transaction.getId());
        paymentOrderMapper.update(order);
 
        log.info("支付宝当面付(第三方接口)创建成功,订单ID: {}, 交易ID: {}", order.getId(), transaction.getId());
 
        return buildPaymentResponse(order, transaction);
    }
 
    /**
     * 查询或创建订单
     */
    private PaymentOrder getOrCreateOrder(PaymentRequest request, String channel) {
        // 先查询是否存在
        PaymentOrder existingOrder = paymentOrderMapper.selectByBizOrderIdAndChannel(
                request.getBizOrderId(), channel);
 
        if (existingOrder != null) {
            // 如果订单已过期,更新状态
            if (existingOrder.isExpired() && OrderStatus.PENDING.getCode().equals(existingOrder.getStatus())) {
                existingOrder.setStatus(OrderStatus.EXPIRED.getCode());
                paymentOrderMapper.update(existingOrder);
            }
            return existingOrder;
        }
 
        // 创建新订单
        PaymentOrder order = new PaymentOrder();
        order.setId(SnowflakeIdGenerator.generateId());
        order.setBizOrderId(request.getBizOrderId());
        order.setAmount(request.getAmount());
        order.setCurrency("CNY");
        order.setChannel(channel);
        order.setStatus(OrderStatus.PENDING.getCode());
        order.setSubject(request.getSubject());
        order.setDescription(request.getDescription());
        order.setCallbackUrl(request.getCallbackUrl());
        order.setExpireAt(LocalDateTime.now().plusHours(2)); // 2小时过期
        order.setVersion(0);
        order.setCreatedAt(LocalDateTime.now());
        order.setUpdatedAt(LocalDateTime.now());
 
        paymentOrderMapper.insert(order);
 
        return order;
    }
 
    /**
     * 调用微信统一下单接口
     */
    private String callWechatUnifiedOrder(PaymentOrder order) {
        try {
            return wxPayV2Client.unifiedOrder(order);
        } catch (Exception e) {
            log.error("调用微信统一下单接口失败", e);
            throw new RuntimeException("微信下单失败: " + e.getMessage(), e);
        }
    }
 
    /**
     * 调用支付宝当面付接口
     */
    private String callAlipayPrecreate(PaymentOrder order) {
        try {
            return alipayF2FClient.precreate(order);
        } catch (Exception e) {
            log.error("调用支付宝当面付接口失败", e);
            throw new RuntimeException("支付宝下单失败: " + e.getMessage(), e);
        }
    }
 
    /**
     * 调用第三方支付宝接口生成支付URL
     */
    private String callAlipayThirdPartyPrecreate(PaymentOrder order, PaymentRequest request) {
        try {
            // 使用AlipayConfig中配置的回调地址
            String notifyUrl = alipayConfig.getNotifyUrl();
            String outTradeNo = String.valueOf(order.getId());
            Integer totalFee = order.getAmount(); // 单位:分
            String serviceOrdId = request.getBizOrderId(); // 业务订单ID
            
            return alipayThirdPartyClient.createQrCodeUrl(notifyUrl, outTradeNo, totalFee, serviceOrdId);
        } catch (Exception e) {
            log.error("调用第三方支付宝接口失败", e);
            throw new RuntimeException("第三方支付宝下单失败: " + e.getMessage(), e);
        }
    }
 
    /**
     * 构造支付响应
     */
    private PaymentResponse buildPaymentResponse(PaymentOrder order, PaymentTransaction transaction) {
        PaymentResponse response = new PaymentResponse();
        response.setOrderId(order.getId());
        response.setTransactionId(transaction.getId());
        response.setStatus(order.getStatus());
        response.setQrBase64(transaction.getQrBase64());
        response.setExpireAt(order.getExpireAt());
        return response;
    }
 
    /**
     * 查询订单
     */
    public PaymentOrder getOrder(Long orderId) {
        return paymentOrderMapper.selectById(orderId);
    }
 
    /**
     * 查询最新交易
     */
    public PaymentTransaction getLatestTransaction(Long orderId) {
        return paymentTransactionMapper.selectLatestByOrderId(orderId);
    }
 
    /**
     * 查询支付宝第三方交易状态
     */
    public String queryAlipayThirdPartyTradeStatus(Long orderId) {
        try {
            // 使用订单ID作为商户订单号
            String outTradeNo = String.valueOf(orderId);
            return alipayThirdPartyClient.queryTradeStatus(outTradeNo);
        } catch (Exception e) {
            log.error("查询支付宝第三方交易状态失败,订单ID: {}", orderId, e);
            throw new RuntimeException("查询交易状态失败: " + e.getMessage(), e);
        }
    }
}