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 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 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 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); } } }