| | |
| | | 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.config.WechatPayConfig; |
| | | import com.ruoyi.payment.infrastructure.persistence.mapper.PaymentOrderMapper; |
| | | import com.ruoyi.payment.infrastructure.persistence.mapper.PaymentTransactionMapper; |
| | | import com.ruoyi.payment.infrastructure.util.QrCodeUtil; |
| | |
| | | @Autowired |
| | | private AlipayConfig alipayConfig; |
| | | |
| | | @Autowired |
| | | private WechatPayConfig wechatPayConfig; |
| | | |
| | | /** |
| | | * 发起微信Native支付 |
| | | */ |
| | |
| | | requestParams.put("bizOrderId", request.getBizOrderId()); |
| | | requestParams.put("amount", request.getAmount()); |
| | | requestParams.put("subject", request.getSubject()); |
| | | requestParams.put("notifyUrl",wechatPayConfig.getNotifyUrl()); |
| | | transaction.setRequestParams(JSON.toJSONString(requestParams)); |
| | | |
| | | paymentTransactionMapper.insert(transaction); |
| | |
| | | requestParams.put("bizOrderId", request.getBizOrderId()); |
| | | requestParams.put("amount", request.getAmount()); |
| | | requestParams.put("subject", request.getSubject()); |
| | | requestParams.put("notifyUrl",alipayConfig.getNotifyUrl()); |
| | | transaction.setRequestParams(JSON.toJSONString(requestParams)); |
| | | |
| | | paymentTransactionMapper.insert(transaction); |
| | |
| | | requestParams.put("amount", request.getAmount()); |
| | | requestParams.put("subject", request.getSubject()); |
| | | requestParams.put("thirdParty", true); |
| | | requestParams.put("notifyUrl",alipayConfig.getThirdParty().getDefaultNotifyUrl()); |
| | | transaction.setRequestParams(JSON.toJSONString(requestParams)); |
| | | |
| | | paymentTransactionMapper.insert(transaction); |
| | |
| | | private String callAlipayThirdPartyPrecreate(PaymentOrder order, PaymentRequest request) { |
| | | try { |
| | | // 使用AlipayConfig中配置的回调地址 |
| | | String notifyUrl = alipayConfig.getNotifyUrl(); |
| | | String notifyUrl = alipayConfig.getThirdParty().getDefaultNotifyUrl(); |
| | | 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); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 轮询查询支付状态(用于回调失败的补偿机制) |
| | | * 支持微信和支付宝 |
| | | * |
| | | * @param orderId 订单ID |
| | | * @return 轮询结果(包含订单状态和交易状态) |
| | | */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Map<String, Object> pollPaymentStatus(Long orderId) { |
| | | log.info("开始轮询查询支付状态,订单ID: {}", orderId); |
| | | |
| | | Map<String, Object> result = new HashMap<>(); |
| | | |
| | | // 1. 查询订单信息 |
| | | PaymentOrder order = paymentOrderMapper.selectById(orderId); |
| | | if (order == null) { |
| | | log.error("订单不存在,订单ID: {}", orderId); |
| | | throw new RuntimeException("订单不存在"); |
| | | } |
| | | |
| | | // 如果订单已经是成功状态,直接返回 |
| | | if (OrderStatus.SUCCEEDED.getCode().equals(order.getStatus())) { |
| | | log.info("订单已成功,无需轮询,订单ID: {}", orderId); |
| | | result.put("orderStatus", order.getStatus()); |
| | | result.put("needPoll", false); |
| | | result.put("message", "订单已成功"); |
| | | return result; |
| | | } |
| | | |
| | | // 2. 查询最新交易记录 |
| | | PaymentTransaction transaction = paymentTransactionMapper.selectLatestByOrderId(orderId); |
| | | if (transaction == null) { |
| | | log.error("未找到交易记录,订单ID: {}", orderId); |
| | | throw new RuntimeException("未找到交易记录"); |
| | | } |
| | | |
| | | // 如果交易已成功,但订单状态未更新,更新订单状态 |
| | | if (TransactionStatus.SUCCEEDED.getCode().equals(transaction.getStatus())) { |
| | | log.info("交易已成功,同步订单状态,订单ID: {}", orderId); |
| | | order.setStatus(OrderStatus.SUCCEEDED.getCode()); |
| | | order.setUpdatedAt(LocalDateTime.now()); |
| | | paymentOrderMapper.update(order); |
| | | |
| | | result.put("orderStatus", OrderStatus.SUCCEEDED.getCode()); |
| | | result.put("transactionStatus", transaction.getStatus()); |
| | | result.put("needPoll", false); |
| | | result.put("message", "支付成功"); |
| | | return result; |
| | | } |
| | | |
| | | // 3. 根据支付渠道调用相应的查询接口 |
| | | String channel = order.getChannel(); |
| | | boolean paymentSuccess = false; |
| | | String tradeStatus = null; |
| | | |
| | | try { |
| | | if (PayChannel.WECHAT.getCode().equals(channel)) { |
| | | // 查询微信支付状态 |
| | | paymentSuccess = queryWechatPaymentStatus(order, transaction); |
| | | tradeStatus = paymentSuccess ? "SUCCESS" : "NOTPAY"; |
| | | result.put("paymethod","WECHAT"); |
| | | } else if (PayChannel.ALIPAY.getCode().equals(channel)) { |
| | | // 判断是否为第三方支付宝 |
| | | boolean isThirdParty = isThirdPartyAlipay(transaction); |
| | | result.put("paymethod","ALIPAY"); |
| | | if (isThirdParty) { |
| | | // 查询第三方支付宝状态 |
| | | tradeStatus = queryAlipayThirdPartyTradeStatus(orderId); |
| | | paymentSuccess = "SUCCESS".equals(tradeStatus); |
| | | } else { |
| | | // 查询官方支付宝状态 |
| | | paymentSuccess = queryAlipayPaymentStatus(order, transaction); |
| | | tradeStatus = paymentSuccess ? "TRADE_SUCCESS" : "WAIT_BUYER_PAY"; |
| | | } |
| | | } else { |
| | | log.error("不支持的支付渠道: {}", channel); |
| | | throw new RuntimeException("不支持的支付渠道"); |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("查询支付状态异常,订单ID: {}", orderId, e); |
| | | result.put("orderStatus", order.getStatus()); |
| | | result.put("transactionStatus", transaction.getStatus()); |
| | | result.put("needPoll", true); |
| | | result.put("error", e.getMessage()); |
| | | result.put("message", "查询异常,请稍后重试"); |
| | | return result; |
| | | } |
| | | |
| | | // 4. 如果支付成功,更新订单和交易状态 |
| | | if (paymentSuccess) { |
| | | log.info("轮询查询发现支付成功,订单ID: {}, 交易状态: {}", orderId, tradeStatus); |
| | | |
| | | // 更新交易状态 |
| | | transaction.setStatus(TransactionStatus.SUCCEEDED.getCode()); |
| | | transaction.setPaidAt(LocalDateTime.now()); |
| | | paymentTransactionMapper.update(transaction); |
| | | |
| | | // 更新订单状态 |
| | | order.setStatus(OrderStatus.SUCCEEDED.getCode()); |
| | | order.setUpdatedAt(LocalDateTime.now()); |
| | | paymentOrderMapper.update(order); |
| | | |
| | | result.put("orderStatus", OrderStatus.SUCCEEDED.getCode()); |
| | | result.put("transactionStatus", TransactionStatus.SUCCEEDED.getCode()); |
| | | result.put("tradeStatus", tradeStatus); |
| | | result.put("needPoll", false); |
| | | result.put("message", "支付成功"); |
| | | |
| | | log.info("轮询查询处理完成,订单和交易状态已更新,订单ID: {}", orderId); |
| | | } else { |
| | | log.info("轮询查询,支付尚未完成,订单ID: {}, 交易状态: {}", orderId, tradeStatus); |
| | | result.put("orderStatus", order.getStatus()); |
| | | result.put("transactionStatus", transaction.getStatus()); |
| | | result.put("tradeStatus", tradeStatus); |
| | | result.put("needPoll", true); |
| | | result.put("message", "支付尚未完成,请继续轮询"); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 查询微信支付状态 |
| | | */ |
| | | private boolean queryWechatPaymentStatus(PaymentOrder order, PaymentTransaction transaction) throws Exception { |
| | | log.info("查询微信支付状态,订单ID: {}", order.getId()); |
| | | |
| | | String outTradeNo = String.valueOf(order.getId()); |
| | | Map<String, String> queryResult = wxPayV2Client.queryOrder(outTradeNo); |
| | | |
| | | // 检查业务结果 |
| | | if (!"SUCCESS".equals(queryResult.get("result_code"))) { |
| | | log.warn("微信订单查询业务失败: {}", queryResult.get("err_code_des")); |
| | | return false; |
| | | } |
| | | |
| | | // 检查交易状态 |
| | | String tradeState = queryResult.get("trade_state"); |
| | | log.info("微信订单状态: {}", tradeState); |
| | | |
| | | return "SUCCESS".equals(tradeState); |
| | | } |
| | | |
| | | /** |
| | | * 查询支付宝支付状态(官方接口) |
| | | */ |
| | | private boolean queryAlipayPaymentStatus(PaymentOrder order, PaymentTransaction transaction) throws Exception { |
| | | log.info("查询支付宝支付状态,订单ID: {}", order.getId()); |
| | | |
| | | String outTradeNo = String.valueOf(order.getId()); |
| | | com.alipay.api.response.AlipayTradeQueryResponse queryResult = alipayF2FClient.queryOrder(outTradeNo); |
| | | |
| | | // 检查交易状态 |
| | | String tradeStatus = queryResult.getTradeStatus(); |
| | | log.info("支付宝订单状态: {}", tradeStatus); |
| | | log.info("支付宝订单查询结果: {}", queryResult.getBody()); |
| | | // TRADE_SUCCESS 或 TRADE_FINISHED 表示支付成功 |
| | | return "TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus); |
| | | } |
| | | |
| | | /** |
| | | * 判断是否为第三方支付宝 |
| | | */ |
| | | private boolean isThirdPartyAlipay(PaymentTransaction transaction) { |
| | | if (transaction.getRequestParams() == null) { |
| | | return false; |
| | | } |
| | | try { |
| | | Map<String, Object> params = JSON.parseObject(transaction.getRequestParams(), Map.class); |
| | | return params.containsKey("thirdParty") && Boolean.TRUE.equals(params.get("thirdParty")); |
| | | } catch (Exception e) { |
| | | log.warn("解析交易请求参数失败", e); |
| | | return false; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 根据交易单号查询微信支付状态 |
| | | * |
| | | * @param outTradeNo 商户订单号(交易单号) |
| | | * @return 交易状态信息 |
| | | */ |
| | | public Map<String, Object> queryWechatTradeByOutTradeNo(String outTradeNo) { |
| | | log.info("根据交易单号查询微信支付状态,商户订单号: {}", outTradeNo); |
| | | |
| | | Map<String, Object> result = new HashMap<>(); |
| | | |
| | | try { |
| | | Map<String, String> queryResult = wxPayV2Client.queryOrder(outTradeNo); |
| | | |
| | | // 检查调用是否成功 |
| | | if (!"SUCCESS".equals(queryResult.get("return_code"))) { |
| | | result.put("success", false); |
| | | result.put("message", "微信查询接口调用失败: " + queryResult.get("return_msg")); |
| | | return result; |
| | | } |
| | | |
| | | // 检查业务结果 |
| | | if (!"SUCCESS".equals(queryResult.get("result_code"))) { |
| | | result.put("success", false); |
| | | result.put("message", "微信订单查询业务失败: " + queryResult.get("err_code_des")); |
| | | result.put("errorCode", queryResult.get("err_code")); |
| | | return result; |
| | | } |
| | | |
| | | // 返回交易信息 |
| | | result.put("success", true); |
| | | result.put("tradeState", queryResult.get("trade_state")); // 交易状态 |
| | | result.put("tradeStateDesc", queryResult.get("trade_state_desc")); // 交易状态描述 |
| | | result.put("outTradeNo", queryResult.get("out_trade_no")); // 商户订单号 |
| | | result.put("transactionId", queryResult.get("transaction_id")); // 微信支付订单号 |
| | | result.put("totalFee", queryResult.get("total_fee")); // 订单金额(分) |
| | | result.put("timeEnd", queryResult.get("time_end")); // 支付完成时间 |
| | | result.put("openid", queryResult.get("openid")); // 用户标识 |
| | | |
| | | // 判断是否支付成功 |
| | | boolean isPaid = "SUCCESS".equals(queryResult.get("trade_state")); |
| | | result.put("isPaid", isPaid); |
| | | result.put("message", isPaid ? "支付成功" : "支付未完成"); |
| | | |
| | | log.info("微信交易查询成功,交易状态: {}", queryResult.get("trade_state")); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("查询微信交易状态异常,商户订单号: {}", outTradeNo, e); |
| | | result.put("success", false); |
| | | result.put("message", "查询异常: " + e.getMessage()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | |
| | | /** |
| | | * 根据交易单号查询支付宝支付状态 |
| | | * |
| | | * @param outTradeNo 商户订单号(交易单号) |
| | | * @return 交易状态信息 |
| | | */ |
| | | public Map<String, Object> queryAlipayTradeByOutTradeNo(String outTradeNo) { |
| | | log.info("根据交易单号查询支付宝支付状态,商户订单号: {}", outTradeNo); |
| | | |
| | | Map<String, Object> result = new HashMap<>(); |
| | | |
| | | try { |
| | | com.alipay.api.response.AlipayTradeQueryResponse queryResult = alipayF2FClient.queryOrder(outTradeNo); |
| | | |
| | | // 检查调用是否成功 |
| | | if (!queryResult.isSuccess()) { |
| | | result.put("success", false); |
| | | result.put("message", "支付宝查询失败: " + queryResult.getSubMsg()); |
| | | result.put("errorCode", queryResult.getSubCode()); |
| | | return result; |
| | | } |
| | | |
| | | // 返回交易信息 |
| | | result.put("success", true); |
| | | result.put("tradeStatus", queryResult.getTradeStatus()); // 交易状态 |
| | | result.put("outTradeNo", queryResult.getOutTradeNo()); // 商户订单号 |
| | | result.put("tradeNo", queryResult.getTradeNo()); // 支付宝交易号 |
| | | result.put("totalAmount", queryResult.getTotalAmount()); // 订单金额(元) |
| | | result.put("buyerPayAmount", queryResult.getBuyerPayAmount()); // 买家实际支付金额 |
| | | result.put("sendPayDate", queryResult.getSendPayDate()); // 交易支付时间 |
| | | result.put("buyerLogonId", queryResult.getBuyerLogonId()); // 买家支付宝账号 |
| | | |
| | | // 判断是否支付成功 |
| | | String tradeStatus = queryResult.getTradeStatus(); |
| | | boolean isPaid = "TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus); |
| | | result.put("isPaid", isPaid); |
| | | |
| | | // 状态说明 |
| | | String message; |
| | | if ("TRADE_SUCCESS".equals(tradeStatus)) { |
| | | message = "交易支付成功"; |
| | | } else if ("TRADE_FINISHED".equals(tradeStatus)) { |
| | | message = "交易结束,不可退款"; |
| | | } else if ("WAIT_BUYER_PAY".equals(tradeStatus)) { |
| | | message = "交易创建,等待买家付款"; |
| | | } else if ("TRADE_CLOSED".equals(tradeStatus)) { |
| | | message = "未付款交易超时关闭,或支付完成后全额退款"; |
| | | } else { |
| | | message = "未知状态: " + tradeStatus; |
| | | } |
| | | result.put("message", message); |
| | | |
| | | log.info("支付宝交易查询成功,交易状态: {}", tradeStatus); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("查询支付宝交易状态异常,商户订单号: {}", outTradeNo, e); |
| | | result.put("success", false); |
| | | result.put("message", "查询异常: " + e.getMessage()); |
| | | } |
| | | |
| | | return result; |
| | | } |
| | | } |