package com.ruoyi.payment.infrastructure.channel.wechat; import com.ruoyi.payment.domain.model.PaymentOrder; import com.ruoyi.payment.infrastructure.config.WechatPayConfig; import lombok.extern.slf4j.Slf4j; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.HashMap; import java.util.Map; /** * 微信支付v2客户端 * * @author ruoyi */ @Slf4j @Component public class WxPayV2Client { private static final String UNIFIED_ORDER_URL = "https://api.mch.weixin.qq.com/pay/unifiedorder"; private static final String ORDER_QUERY_URL = "https://api.mch.weixin.qq.com/pay/orderquery"; private static final String CLOSE_ORDER_URL = "https://api.mch.weixin.qq.com/pay/closeorder"; @Autowired private WechatPayConfig wechatPayConfig; @Autowired private WxPayUtil wxPayUtil; /** * 统一下单 - Native支付 */ public String unifiedOrder(PaymentOrder order) throws Exception { log.info("调用微信统一下单接口,订单ID: {}", order.getId()); // 构造请求参数 Map params = new HashMap<>(); params.put("appid", wechatPayConfig.getAppId()); params.put("mch_id", wechatPayConfig.getMchId()); params.put("nonce_str", generateNonceStr()); params.put("body", order.getSubject()); params.put("out_trade_no", String.valueOf(order.getId())); // 使用订单ID作为商户订单号 params.put("total_fee", String.valueOf(order.getAmount())); // 单位:分 params.put("spbill_create_ip", "127.0.0.1"); params.put("notify_url", wechatPayConfig.getNotifyUrl()); params.put("trade_type", "NATIVE"); if (order.getDescription() != null && !order.getDescription().isEmpty()) { params.put("detail", order.getDescription()); } // 生成签名 String sign = wxPayUtil.generateSign(params); params.put("sign", sign); // 转换为XML String requestXml = wxPayUtil.mapToXml(params); log.info("微信统一下单请求: {}", requestXml); // 发送HTTP请求 String responseXml = doPost(UNIFIED_ORDER_URL, requestXml); log.info("微信统一下单响应: {}", responseXml); // 解析响应 Map response = wxPayUtil.xmlToMap(responseXml); // 验证响应签名 if (!wxPayUtil.verifySign(response)) { throw new RuntimeException("微信返回签名验证失败"); } // 检查返回状态 if (!"SUCCESS".equals(response.get("return_code"))) { throw new RuntimeException("微信统一下单失败: " + response.get("return_msg")); } if (!"SUCCESS".equals(response.get("result_code"))) { throw new RuntimeException("微信统一下单业务失败: " + response.get("err_code_des")); } // 返回code_url String codeUrl = response.get("code_url"); if (codeUrl == null || codeUrl.isEmpty()) { throw new RuntimeException("微信未返回code_url"); } log.info("微信统一下单成功,code_url: {}", codeUrl); return codeUrl; } /** * 查询订单 */ public Map queryOrder(String outTradeNo) throws Exception { log.info("查询微信订单,商户订单号: {}", outTradeNo); Map params = new HashMap<>(); params.put("appid", wechatPayConfig.getAppId()); params.put("mch_id", wechatPayConfig.getMchId()); params.put("out_trade_no", outTradeNo); params.put("nonce_str", generateNonceStr()); String sign = wxPayUtil.generateSign(params); params.put("sign", sign); String requestXml = wxPayUtil.mapToXml(params); String responseXml = doPost(ORDER_QUERY_URL, requestXml); Map response = wxPayUtil.xmlToMap(responseXml); if (!wxPayUtil.verifySign(response)) { throw new RuntimeException("查询订单响应签名验证失败"); } if (!"SUCCESS".equals(response.get("return_code"))) { throw new RuntimeException("查询订单失败: " + response.get("return_msg")); } return response; } /** * 关闭订单 */ public void closeOrder(String outTradeNo) throws Exception { log.info("关闭微信订单,商户订单号: {}", outTradeNo); Map params = new HashMap<>(); params.put("appid", wechatPayConfig.getAppId()); params.put("mch_id", wechatPayConfig.getMchId()); params.put("out_trade_no", outTradeNo); params.put("nonce_str", generateNonceStr()); String sign = wxPayUtil.generateSign(params); params.put("sign", sign); String requestXml = wxPayUtil.mapToXml(params); String responseXml = doPost(CLOSE_ORDER_URL, requestXml); Map response = wxPayUtil.xmlToMap(responseXml); if (!"SUCCESS".equals(response.get("return_code"))) { throw new RuntimeException("关闭订单失败: " + response.get("return_msg")); } log.info("关闭微信订单成功"); } /** * 发送POST请求 */ private String doPost(String url, String xmlData) throws Exception { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { HttpPost httpPost = new HttpPost(url); httpPost.setHeader("Content-Type", "text/xml;charset=UTF-8"); httpPost.setEntity(new StringEntity(xmlData, "UTF-8")); try (CloseableHttpResponse response = httpClient.execute(httpPost)) { HttpEntity entity = response.getEntity(); return EntityUtils.toString(entity, "UTF-8"); } } } /** * 生成随机字符串 */ private String generateNonceStr() { return java.util.UUID.randomUUID().toString().replace("-", ""); } }