From 4adb656ffd2c3660e07d224dd483e7479d48b46e Mon Sep 17 00:00:00 2001 From: 无关风月 <443237572@qq.com> Date: 星期三, 30 四月 2025 17:58:57 +0800 Subject: [PATCH] bug修改 --- cloud-server-activity/src/main/java/com/dsh/activity/util/wx/WxAppPayService.java | 443 +++++++++++++++++++++++++++--------------------------- 1 files changed, 222 insertions(+), 221 deletions(-) diff --git a/cloud-server-activity/src/main/java/com/dsh/activity/util/wx/WxAppPayService.java b/cloud-server-activity/src/main/java/com/dsh/activity/util/wx/WxAppPayService.java index 4d53065..0b243e3 100644 --- a/cloud-server-activity/src/main/java/com/dsh/activity/util/wx/WxAppPayService.java +++ b/cloud-server-activity/src/main/java/com/dsh/activity/util/wx/WxAppPayService.java @@ -1,221 +1,222 @@ -package com.dsh.activity.util.wx; - -import com.dsh.activity.util.wx.WXPayConstants; -import com.dsh.activity.util.wx.WXPaySignatureCertificateUtil; -import com.dsh.activity.util.wx.WxV3PayConfig; -import com.fasterxml.jackson.databind.JsonNode; // 引入Jackson库处理JSON -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -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.util.EntityUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import java.io.IOException; -import java.math.BigDecimal; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; - -@Service -public class WxAppPayService { - - private static final Logger log = LoggerFactory.getLogger(WxAppPayService.class); - - private final ObjectMapper objectMapper; // 用于JSON序列化和反序列化 - private final CloseableHttpClient wechatPayClient; // 注入通过工具类创建的HTTP客户端 - - @Autowired - public WxAppPayService(ObjectMapper objectMapper) throws IOException { - this.objectMapper = objectMapper; - // 在构造函数中初始化带有签名验证功能的HTTP客户端 - this.wechatPayClient = WXPaySignatureCertificateUtil.getWechatPayClient(); - log.info("微信支付V3 HTTP客户端初始化完成。"); - } - - /** - * 创建APP支付预付单 (统一下单) - * - * @param description 商品描述 - * @param outTradeNo 商户订单号 (要求32个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下唯一) - * @param totalAmount 支付总金额 (单位:元) - * @return 用于调起APP支付的参数 Map (appid, partnerid, prepayid, package, noncestr, timestamp, sign) - * @throws IOException 网络或IO异常 - * @throws RuntimeException 支付请求失败或处理异常 - */ - public Map<String, String> createOrder(String description, String outTradeNo, BigDecimal totalAmount) - throws IOException { - - // 1. 构建请求URL - String url = WXPayConstants.DOMAIN_API + WXPayConstants.PAY_TRANSACTIONS_APP; - HttpPost httpPost = new HttpPost(url); - httpPost.addHeader("Accept", "application/json"); - httpPost.addHeader("Content-type", "application/json; charset=utf-8"); - - // 2. 构建请求体 JSON - ObjectNode rootNode = objectMapper.createObjectNode(); -// rootNode.put("appid", WxV3PayConfig.APP_ID);//服务商不需要该字段 -// rootNode.put("mchid", WxV3PayConfig.Mch_ID);//服务商不需要该字段 - rootNode.put("description", description); - rootNode.put("out_trade_no", outTradeNo); - rootNode.put("notify_url", WXPayConstants.WECHAT_PAY_NOTIFY_URL); // 使用常量中的回调地址 - - ObjectNode amountNode = objectMapper.createObjectNode(); - // 微信支付金额单位为分,需要转换 - amountNode.put("total", totalAmount.multiply(new BigDecimal("100")).intValue()); - amountNode.put("currency", "CNY"); // 货币类型,默认人民币 - rootNode.set("amount", amountNode); - - // 如果你是服务商模式,需要添加子商户信息 - rootNode.put("sp_mchid", WxV3PayConfig.Mch_ID); - rootNode.put("sub_mchid", "123"); - rootNode.put("sp_appid", "wx41d32f362ba0f911"); - // rootNode.put("sub_appid", "子商户AppID"); // 如果子商户需要用自己的AppID拉起支付 - - String requestBody = rootNode.toString(); - log.info("微信APP支付下单请求体: {}", requestBody); - httpPost.setEntity(new StringEntity(requestBody, StandardCharsets.UTF_8)); - - // 3. 发送请求 - try (CloseableHttpResponse response = wechatPayClient.execute(httpPost)) { - int statusCode = response.getStatusLine().getStatusCode(); - String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); - log.info("微信APP支付下单响应状态码: {}, 响应体: {}", statusCode, responseBody); - - if (statusCode == 200) { // HTTP状态码 200 表示成功 - JsonNode responseNode = objectMapper.readTree(responseBody); - String prepayId = responseNode.get("prepay_id").asText(); - log.info("成功获取预支付交易会话标识 (prepay_id): {}", prepayId); - - // 4. 生成调起支付所需的参数 - Map<String, String> payParams = WXPaySignatureCertificateUtil.buildAppPayParams(prepayId); - log.info("生成APP支付参数: {}", payParams); - return payParams; - - } else { - // 处理错误情况 - log.error("微信APP支付下单失败,状态码: {}, 响应: {}", statusCode, responseBody); - // 可以根据 responseBody 中的 code 和 message 进一步分析错误 - throw new RuntimeException("微信APP支付下单失败: " + responseBody); - } - } catch (Exception e) { - log.error("微信APP支付下单请求执行异常", e); - throw new IOException("微信APP支付下单请求执行异常", e); - } - } - - /** - * 关闭订单 (根据商户订单号) - * - * @param outTradeNo 商户订单号 - * @return true 如果关闭成功或订单已关闭/不存在 (根据微信文档,成功是204 No Content) - * @throws IOException 网络或IO异常 - * @throws RuntimeException 关闭请求失败或处理异常 - */ - public boolean closeOrder(String outTradeNo) throws IOException { - // 1. 构建请求URL, 注意替换占位符 - String url = WXPayConstants.DOMAIN_API - + WXPayConstants.PAY_TRANSACTIONS_OUT_TRADE_NO.replace("{}", outTradeNo); - - HttpPost httpPost = new HttpPost(url); - httpPost.addHeader("Accept", "application/json"); - httpPost.addHeader("Content-type", "application/json; charset=utf-8"); - - // 2. 构建请求体 JSON (只需要商户号) - ObjectNode rootNode = objectMapper.createObjectNode(); - rootNode.put("mchid", WxV3PayConfig.Mch_ID); - // 如果是服务商模式,则用 sp_mchid - rootNode.put("sp_mchid", WxV3PayConfig.Mch_ID); - - String requestBody = rootNode.toString(); - log.info("微信关单请求 URL: {}, 请求体: {}", url, requestBody); - httpPost.setEntity(new StringEntity(requestBody, StandardCharsets.UTF_8)); - - // 3. 发送请求 - try (CloseableHttpResponse response = wechatPayClient.execute(httpPost)) { - int statusCode = response.getStatusLine().getStatusCode(); - // 关单成功时,微信返回 HTTP 状态码 204 No Content - log.info("微信关单响应状态码: {}", statusCode); - if (statusCode == 204) { - log.info("订单 {} 关闭成功。", outTradeNo); - return true; - } else { - String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); - log.error("微信关单失败,商户订单号: {}, 状态码: {}, 响应: {}", outTradeNo, statusCode, responseBody); - // 根据需要可以解析responseBody获取错误详情 - return false; // 或者抛出异常,根据业务决定 - } - } catch (Exception e) { - log.error("微信关单请求执行异常, 商户订单号: {}", outTradeNo, e); - throw new IOException("微信关单请求执行异常", e); - } - } - - - // --- 退款相关方法 (可选) --- - - /** - * 申请退款 - * - * @param outTradeNo 原商户订单号 - * @param outRefundNo 商户退款单号 (商户系统内部的退款单号,要求唯一) - * @param reason 退款原因 (可选) - * @param refundAmount 退款金额 (单位:元) - * @param totalAmount 原订单总金额 (单位:元) - * @return 退款处理结果,可以返回微信返回的JSON节点或自定义对象 - * @throws IOException 网络或IO异常 - * @throws RuntimeException 退款请求失败或处理异常 - */ - public JsonNode createRefund(String outTradeNo, String outRefundNo, String reason, - BigDecimal refundAmount, BigDecimal totalAmount) throws IOException { - - String url = WXPayConstants.DOMAIN_API + WXPayConstants.REFUND_DOMESTIC_REFUNDS; - HttpPost httpPost = new HttpPost(url); - httpPost.addHeader("Accept", "application/json"); - httpPost.addHeader("Content-type", "application/json; charset=utf-8"); - - ObjectNode rootNode = objectMapper.createObjectNode(); - // 如果是服务商模式,需要添加子商户号 - // rootNode.put("sub_mchid", "子商户号"); - rootNode.put("out_trade_no", outTradeNo); - rootNode.put("out_refund_no", outRefundNo); - if (reason != null && !reason.isEmpty()) { - rootNode.put("reason", reason); - } - // 设置退款回调地址 - rootNode.put("notify_url", WXPayConstants.WECHAT_REFUNDS_NOTIFY_URL); - - ObjectNode amountNode = objectMapper.createObjectNode(); - amountNode.put("refund", refundAmount.multiply(new BigDecimal("100")).intValue()); // 退款金额,分 - amountNode.put("total", totalAmount.multiply(new BigDecimal("100")).intValue()); // 原订单金额,分 - amountNode.put("currency", "CNY"); - rootNode.set("amount", amountNode); - - String requestBody = rootNode.toString(); - log.info("微信申请退款请求体: {}", requestBody); - httpPost.setEntity(new StringEntity(requestBody, StandardCharsets.UTF_8)); - - try (CloseableHttpResponse response = wechatPayClient.execute(httpPost)) { - int statusCode = response.getStatusLine().getStatusCode(); - String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); - log.info("微信申请退款响应状态码: {}, 响应体: {}", statusCode, responseBody); - - if (statusCode == 200) { // 成功受理 - log.info("微信退款申请成功受理。"); - return objectMapper.readTree(responseBody); - } else { - log.error("微信申请退款失败,状态码: {}, 响应: {}", statusCode, responseBody); - throw new RuntimeException("微信申请退款失败: " + responseBody); - } - } catch (Exception e) { - log.error("微信申请退款请求执行异常", e); - throw new IOException("微信申请退款请求执行异常", e); - } - } - -} \ No newline at end of file +//package com.dsh.activity.util.wx; +// +//import com.dsh.activity.util.wx.WXPayConstants; +//import com.dsh.activity.util.wx.WXPaySignatureCertificateUtil; +//import com.dsh.activity.util.wx.WxV3PayConfig; +//import com.fasterxml.jackson.databind.JsonNode; // 引入Jackson库处理JSON +//import com.fasterxml.jackson.databind.ObjectMapper; +//import com.fasterxml.jackson.databind.node.ObjectNode; +//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.util.EntityUtils; +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.stereotype.Component; +//import org.springframework.stereotype.Service; +// +//import java.io.IOException; +//import java.math.BigDecimal; +//import java.nio.charset.StandardCharsets; +//import java.util.HashMap; +//import java.util.Map; +// +//@Component +//public class WxAppPayService { +// +// private static final Logger log = LoggerFactory.getLogger(WxAppPayService.class); +// +// private final ObjectMapper objectMapper; // 用于JSON序列化和反序列化 +// private final CloseableHttpClient wechatPayClient; // 注入通过工具类创建的HTTP客户端 +// +// @Autowired +// public WxAppPayService(ObjectMapper objectMapper) throws IOException { +// this.objectMapper = objectMapper; +// // 在构造函数中初始化带有签名验证功能的HTTP客户端 +// this.wechatPayClient = WXPaySignatureCertificateUtil.getWechatPayClient(); +// log.info("微信支付V3 HTTP客户端初始化完成。"); +// } +// +// /** +// * 创建APP支付预付单 (统一下单) +// * +// * @param description 商品描述 +// * @param outTradeNo 商户订单号 (要求32个字符内,只能是数字、大小写字母_-|* 且在同一个商户号下唯一) +// * @param totalAmount 支付总金额 (单位:元) +// * @return 用于调起APP支付的参数 Map (appid, partnerid, prepayid, package, noncestr, timestamp, sign) +// * @throws IOException 网络或IO异常 +// * @throws RuntimeException 支付请求失败或处理异常 +// */ +// public Map<String, String> createOrder(String description, String outTradeNo, BigDecimal totalAmount) +// throws IOException { +// +// // 1. 构建请求URL +// String url = WXPayConstants.DOMAIN_API + WXPayConstants.PAY_TRANSACTIONS_APP; +// HttpPost httpPost = new HttpPost(url); +// httpPost.addHeader("Accept", "application/json"); +// httpPost.addHeader("Content-type", "application/json; charset=utf-8"); +// +// // 2. 构建请求体 JSON +// ObjectNode rootNode = objectMapper.createObjectNode(); +//// rootNode.put("appid", WxV3PayConfig.APP_ID);//服务商不需要该字段 +//// rootNode.put("mchid", WxV3PayConfig.Mch_ID);//服务商不需要该字段 +// rootNode.put("description", description); +// rootNode.put("out_trade_no", outTradeNo); +// rootNode.put("notify_url", WXPayConstants.WECHAT_PAY_NOTIFY_URL); // 使用常量中的回调地址 +// +// ObjectNode amountNode = objectMapper.createObjectNode(); +// // 微信支付金额单位为分,需要转换 +// amountNode.put("total", totalAmount.multiply(new BigDecimal("100")).intValue()); +// amountNode.put("currency", "CNY"); // 货币类型,默认人民币 +// rootNode.set("amount", amountNode); +// +// // 如果你是服务商模式,需要添加子商户信息 +// rootNode.put("sp_mchid", WxV3PayConfig.Mch_ID); +// rootNode.put("sub_mchid", "123"); +// rootNode.put("sp_appid", "wx41d32f362ba0f911"); +// // rootNode.put("sub_appid", "子商户AppID"); // 如果子商户需要用自己的AppID拉起支付 +// +// String requestBody = rootNode.toString(); +// log.info("微信APP支付下单请求体: {}", requestBody); +// httpPost.setEntity(new StringEntity(requestBody, StandardCharsets.UTF_8)); +// +// // 3. 发送请求 +// try (CloseableHttpResponse response = wechatPayClient.execute(httpPost)) { +// int statusCode = response.getStatusLine().getStatusCode(); +// String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); +// log.info("微信APP支付下单响应状态码: {}, 响应体: {}", statusCode, responseBody); +// +// if (statusCode == 200) { // HTTP状态码 200 表示成功 +// JsonNode responseNode = objectMapper.readTree(responseBody); +// String prepayId = responseNode.get("prepay_id").asText(); +// log.info("成功获取预支付交易会话标识 (prepay_id): {}", prepayId); +// +// // 4. 生成调起支付所需的参数 +// Map<String, String> payParams = WXPaySignatureCertificateUtil.buildAppPayParams(prepayId); +// log.info("生成APP支付参数: {}", payParams); +// return payParams; +// +// } else { +// // 处理错误情况 +// log.error("微信APP支付下单失败,状态码: {}, 响应: {}", statusCode, responseBody); +// // 可以根据 responseBody 中的 code 和 message 进一步分析错误 +// throw new RuntimeException("微信APP支付下单失败: " + responseBody); +// } +// } catch (Exception e) { +// log.error("微信APP支付下单请求执行异常", e); +// throw new IOException("微信APP支付下单请求执行异常", e); +// } +// } +// +// /** +// * 关闭订单 (根据商户订单号) +// * +// * @param outTradeNo 商户订单号 +// * @return true 如果关闭成功或订单已关闭/不存在 (根据微信文档,成功是204 No Content) +// * @throws IOException 网络或IO异常 +// * @throws RuntimeException 关闭请求失败或处理异常 +// */ +// public boolean closeOrder(String outTradeNo) throws IOException { +// // 1. 构建请求URL, 注意替换占位符 +// String url = WXPayConstants.DOMAIN_API +// + WXPayConstants.PAY_TRANSACTIONS_OUT_TRADE_NO.replace("{}", outTradeNo); +// +// HttpPost httpPost = new HttpPost(url); +// httpPost.addHeader("Accept", "application/json"); +// httpPost.addHeader("Content-type", "application/json; charset=utf-8"); +// +// // 2. 构建请求体 JSON (只需要商户号) +// ObjectNode rootNode = objectMapper.createObjectNode(); +// rootNode.put("mchid", WxV3PayConfig.Mch_ID); +// // 如果是服务商模式,则用 sp_mchid +// rootNode.put("sp_mchid", WxV3PayConfig.Mch_ID); +// +// String requestBody = rootNode.toString(); +// log.info("微信关单请求 URL: {}, 请求体: {}", url, requestBody); +// httpPost.setEntity(new StringEntity(requestBody, StandardCharsets.UTF_8)); +// +// // 3. 发送请求 +// try (CloseableHttpResponse response = wechatPayClient.execute(httpPost)) { +// int statusCode = response.getStatusLine().getStatusCode(); +// // 关单成功时,微信返回 HTTP 状态码 204 No Content +// log.info("微信关单响应状态码: {}", statusCode); +// if (statusCode == 204) { +// log.info("订单 {} 关闭成功。", outTradeNo); +// return true; +// } else { +// String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); +// log.error("微信关单失败,商户订单号: {}, 状态码: {}, 响应: {}", outTradeNo, statusCode, responseBody); +// // 根据需要可以解析responseBody获取错误详情 +// return false; // 或者抛出异常,根据业务决定 +// } +// } catch (Exception e) { +// log.error("微信关单请求执行异常, 商户订单号: {}", outTradeNo, e); +// throw new IOException("微信关单请求执行异常", e); +// } +// } +// +// +// // --- 退款相关方法 (可选) --- +// +// /** +// * 申请退款 +// * +// * @param outTradeNo 原商户订单号 +// * @param outRefundNo 商户退款单号 (商户系统内部的退款单号,要求唯一) +// * @param reason 退款原因 (可选) +// * @param refundAmount 退款金额 (单位:元) +// * @param totalAmount 原订单总金额 (单位:元) +// * @return 退款处理结果,可以返回微信返回的JSON节点或自定义对象 +// * @throws IOException 网络或IO异常 +// * @throws RuntimeException 退款请求失败或处理异常 +// */ +// public JsonNode createRefund(String outTradeNo, String outRefundNo, String reason, +// BigDecimal refundAmount, BigDecimal totalAmount) throws IOException { +// +// String url = WXPayConstants.DOMAIN_API + WXPayConstants.REFUND_DOMESTIC_REFUNDS; +// HttpPost httpPost = new HttpPost(url); +// httpPost.addHeader("Accept", "application/json"); +// httpPost.addHeader("Content-type", "application/json; charset=utf-8"); +// +// ObjectNode rootNode = objectMapper.createObjectNode(); +// // 如果是服务商模式,需要添加子商户号 +// // rootNode.put("sub_mchid", "子商户号"); +// rootNode.put("out_trade_no", outTradeNo); +// rootNode.put("out_refund_no", outRefundNo); +// if (reason != null && !reason.isEmpty()) { +// rootNode.put("reason", reason); +// } +// // 设置退款回调地址 +// rootNode.put("notify_url", WXPayConstants.WECHAT_REFUNDS_NOTIFY_URL); +// +// ObjectNode amountNode = objectMapper.createObjectNode(); +// amountNode.put("refund", refundAmount.multiply(new BigDecimal("100")).intValue()); // 退款金额,分 +// amountNode.put("total", totalAmount.multiply(new BigDecimal("100")).intValue()); // 原订单金额,分 +// amountNode.put("currency", "CNY"); +// rootNode.set("amount", amountNode); +// +// String requestBody = rootNode.toString(); +// log.info("微信申请退款请求体: {}", requestBody); +// httpPost.setEntity(new StringEntity(requestBody, StandardCharsets.UTF_8)); +// +// try (CloseableHttpResponse response = wechatPayClient.execute(httpPost)) { +// int statusCode = response.getStatusLine().getStatusCode(); +// String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); +// log.info("微信申请退款响应状态码: {}, 响应体: {}", statusCode, responseBody); +// +// if (statusCode == 200) { // 成功受理 +// log.info("微信退款申请成功受理。"); +// return objectMapper.readTree(responseBody); +// } else { +// log.error("微信申请退款失败,状态码: {}, 响应: {}", statusCode, responseBody); +// throw new RuntimeException("微信申请退款失败: " + responseBody); +// } +// } catch (Exception e) { +// log.error("微信申请退款请求执行异常", e); +// throw new IOException("微信申请退款请求执行异常", e); +// } +// } +// +//} \ No newline at end of file -- Gitblit v1.7.1