From 8267b410a53847baa997094d67c94826894f0f25 Mon Sep 17 00:00:00 2001 From: tianyongbao Date: Sat, 4 Apr 2026 01:01:39 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=BE=AE=E4=BF=A1=E6=94=AF=E4=BB=98?= =?UTF-8?q?=EF=BC=8C=E6=94=B9=E4=B8=BAhttp=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../weixin/config/WxPayConfiguration.java | 70 +-- .../weixin/service/impl/WxPayServiceImpl.java | 444 +++++++++--------- 2 files changed, 218 insertions(+), 296 deletions(-) diff --git a/intc-modules/intc-weixin/src/main/java/com/intc/weixin/config/WxPayConfiguration.java b/intc-modules/intc-weixin/src/main/java/com/intc/weixin/config/WxPayConfiguration.java index 5196b3c..f02cba7 100644 --- a/intc-modules/intc-weixin/src/main/java/com/intc/weixin/config/WxPayConfiguration.java +++ b/intc-modules/intc-weixin/src/main/java/com/intc/weixin/config/WxPayConfiguration.java @@ -1,81 +1,19 @@ package com.intc.weixin.config; -import com.github.binarywang.wxpay.config.WxPayConfig; -import com.github.binarywang.wxpay.service.WxPayService; -import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** - * 微信支付配置 + * 微信支付配置(原生HTTP实现,无需SDK Bean) + * 配置参数通过 WxPayProperties 和 WxMaProperties 自动注入 * * @author intc */ @Slf4j @Configuration -@RequiredArgsConstructor @ConditionalOnProperty(prefix = "wx.pay", name = "mch-id") public class WxPayConfiguration { - - private final WxPayProperties wxPayProperties; - private final WxMaProperties wxMaProperties; - - @Bean - public WxPayService wxPayService() { - WxPayConfig payConfig = new WxPayConfig(); - - // 基础配置 - payConfig.setAppId(wxMaProperties.getAppId()); - payConfig.setMchId(wxPayProperties.getMchId()); - - // V2配置 - if (wxPayProperties.getMchKey() != null) { - payConfig.setMchKey(wxPayProperties.getMchKey()); - } - - // V3配置 - if (wxPayProperties.getApiV3Key() != null) { - payConfig.setApiV3Key(wxPayProperties.getApiV3Key()); - } - - if (wxPayProperties.getCertSerialNo() != null) { - payConfig.setCertSerialNo(wxPayProperties.getCertSerialNo()); - } - - // 证书配置 - if (wxPayProperties.getKeyPath() != null) { - payConfig.setKeyPath(wxPayProperties.getKeyPath()); - } - - // 私钥配置:优先使用privateContent,避免文件不存在的问题 - if (wxPayProperties.getPrivateContent() != null && !wxPayProperties.getPrivateContent().isEmpty()) { - payConfig.setPrivateKeyContent(wxPayProperties.getPrivateContent().getBytes(java.nio.charset.StandardCharsets.UTF_8)); - log.info("使用私钥内容配置"); - } else if (wxPayProperties.getPrivateKeyPath() != null) { - payConfig.setPrivateKeyPath(wxPayProperties.getPrivateKeyPath()); - log.info("使用私钥路径配置: {}", wxPayProperties.getPrivateKeyPath()); - } - - // 商户证书配置(apiclient_cert.pem),有真实PEM内容才设置,避免占位符导致v3请求构造异常 - String privateCertContent = wxPayProperties.getPrivateCertContent(); - if (privateCertContent != null && privateCertContent.startsWith("-----BEGIN CERTIFICATE-----")) { - payConfig.setPrivateCertContent(privateCertContent.getBytes(java.nio.charset.StandardCharsets.UTF_8)); - log.info("使用商户证书内容配置"); - } else if (wxPayProperties.getPrivateCertPath() != null) { - payConfig.setPrivateCertPath(wxPayProperties.getPrivateCertPath()); - log.info("使用商户证书路径配置: {}", wxPayProperties.getPrivateCertPath()); - } else { - log.info("未配置商户证书,将使用私钥直接签名"); - } - - WxPayService wxPayService = new WxPayServiceImpl(); - wxPayService.setConfig(payConfig); - - log.info("微信支付服务初始化完成,商户号: {}", wxPayProperties.getMchId()); - - return wxPayService; - } + // 原生HTTP实现,无需初始化SDK的WxPayService Bean + // 配置参数自动注入到 WxPayProperties 和 WxMaProperties } diff --git a/intc-modules/intc-weixin/src/main/java/com/intc/weixin/service/impl/WxPayServiceImpl.java b/intc-modules/intc-weixin/src/main/java/com/intc/weixin/service/impl/WxPayServiceImpl.java index 2d99898..a56f995 100644 --- a/intc-modules/intc-weixin/src/main/java/com/intc/weixin/service/impl/WxPayServiceImpl.java +++ b/intc-modules/intc-weixin/src/main/java/com/intc/weixin/service/impl/WxPayServiceImpl.java @@ -1,23 +1,31 @@ package com.intc.weixin.service.impl; import cn.hutool.core.util.RandomUtil; -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; -import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result; -import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; -import com.github.binarywang.wxpay.exception.WxPayException; -import com.github.binarywang.wxpay.v3.util.AesUtils; +import com.intc.weixin.config.WxPayProperties; +import com.intc.weixin.config.WxMaProperties; import com.intc.weixin.service.WxPayService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import javax.crypto.Cipher; +import javax.crypto.spec.GCMParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; import java.util.HashMap; import java.util.Map; /** - * 微信支付服务实现 + * 微信支付服务实现(原生HTTP+RSA私钥签名,无需apiclient_cert.pem) * * @author intc */ @@ -26,154 +34,172 @@ import java.util.Map; @RequiredArgsConstructor public class WxPayServiceImpl implements WxPayService { - @Autowired(required = false) - private com.github.binarywang.wxpay.service.WxPayService wxPayService; + private static final String JSAPI_URL = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi"; + private static final String CLOSE_URL_TEMPLATE = "https://api.mch.weixin.qq.com/v3/pay/transactions/out-trade-no/%s/close"; + + private final WxPayProperties wxPayProperties; + private final WxMaProperties wxMaProperties; + + // ---------------------------------------------------------------- + // 私钥加载(懒加载,只解析一次) + // ---------------------------------------------------------------- + private volatile PrivateKey cachedPrivateKey; + + private PrivateKey getPrivateKey() { + if (cachedPrivateKey != null) { + return cachedPrivateKey; + } + synchronized (this) { + if (cachedPrivateKey != null) { + return cachedPrivateKey; + } + try { + String content = wxPayProperties.getPrivateContent(); + if (content == null || content.isEmpty()) { + throw new IllegalStateException("wx.pay.private-content 未配置"); + } + // 去掉 PEM 头尾和换行 + String pem = content + .replace("-----BEGIN PRIVATE KEY-----", "") + .replace("-----END PRIVATE KEY-----", "") + .replaceAll("\\s+", ""); + byte[] keyBytes = Base64.getDecoder().decode(pem); + PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); + cachedPrivateKey = KeyFactory.getInstance("RSA").generatePrivate(spec); + log.info("微信支付私钥加载成功"); + return cachedPrivateKey; + } catch (Exception e) { + throw new RuntimeException("加载微信支付私钥失败", e); + } + } + } + + // ---------------------------------------------------------------- + // RSA-SHA256 签名 + // ---------------------------------------------------------------- + private String rsaSign(String content) { + try { + Signature signer = Signature.getInstance("SHA256withRSA"); + signer.initSign(getPrivateKey()); + signer.update(content.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(signer.sign()); + } catch (Exception e) { + throw new RuntimeException("微信支付签名失败", e); + } + } + + // ---------------------------------------------------------------- + // 构造 Authorization 请求头 + // ---------------------------------------------------------------- + private String buildAuthorization(String method, String urlPath, String body) { + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); + String nonce = RandomUtil.randomString(32); + String message = method + "\n" + urlPath + "\n" + timestamp + "\n" + nonce + "\n" + body + "\n"; + String signature = rsaSign(message); + return String.format( + "WECHATPAY2-SHA256-RSA2048 mchid=\"%s\",nonce_str=\"%s\",timestamp=\"%s\",serial_no=\"%s\",signature=\"%s\"", + wxPayProperties.getMchId(), nonce, timestamp, + wxPayProperties.getCertSerialNo(), signature + ); + } + + // ---------------------------------------------------------------- + // 发送 HTTP POST + // ---------------------------------------------------------------- + private String doPost(String url, String jsonBody) throws Exception { + URI uri = URI.create(url); + String urlPath = uri.getPath(); + String authorization = buildAuthorization("POST", urlPath, jsonBody); + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(uri) + .header("Content-Type", "application/json") + .header("Accept", "application/json") + .header("Authorization", authorization) + .POST(HttpRequest.BodyPublishers.ofString(jsonBody, StandardCharsets.UTF_8)) + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + log.info("微信支付API响应: status={}, body={}", response.statusCode(), response.body()); + if (response.statusCode() < 200 || response.statusCode() >= 300) { + throw new RuntimeException("微信支付请求失败: " + response.statusCode() + " " + response.body()); + } + return response.body(); + } + + // ---------------------------------------------------------------- + // 发送 HTTP POST(无响应体,如关闭订单) + // ---------------------------------------------------------------- + private void doPostNoBody(String url, String jsonBody) throws Exception { + URI uri = URI.create(url); + String urlPath = uri.getPath(); + String authorization = buildAuthorization("POST", urlPath, jsonBody); + HttpClient client = HttpClient.newHttpClient(); + HttpRequest request = HttpRequest.newBuilder() + .uri(uri) + .header("Content-Type", "application/json") + .header("Accept", "application/json") + .header("Authorization", authorization) + .POST(HttpRequest.BodyPublishers.ofString(jsonBody, StandardCharsets.UTF_8)) + .build(); + HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString(StandardCharsets.UTF_8)); + log.info("关闭订单响应: status={}", response.statusCode()); + if (response.statusCode() >= 300) { + throw new RuntimeException("关闭订单失败: " + response.statusCode() + " " + response.body()); + } + } + + // ---------------------------------------------------------------- + // 接口实现 + // ---------------------------------------------------------------- @Override public String createJsapiOrder(String openId, String outTradeNumber, Integer totalAmount, String description, String notifyUrl) { - log.info("创建 JSAPI支付订单: openId={}, outTradeNumber={}, totalAmount={}, description={}", - openId, outTradeNumber, totalAmount, description); - - // 检查微信支付服务是否可用 - if (wxPayService == null) { - log.warn("微信支付SDK未配置,返回模拟数据"); - return "mock_prepay_id_" + System.currentTimeMillis(); - } - + log.info("创建JSAPI支付订单: openId={}, outTradeNumber={}, amount={}", openId, outTradeNumber, totalAmount); try { - // 构建请求对象 - WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request(); - request.setOutTradeNo(outTradeNumber); - request.setDescription(description); - request.setNotifyUrl(notifyUrl); - - // 设置金额 - WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount(); - amount.setTotal(totalAmount); - amount.setCurrency("CNY"); - request.setAmount(amount); - - // 设置支付者 - WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer(); - payer.setOpenid(openId); - request.setPayer(payer); - - // 调用微信支付API - 注意:4.6.5.B 版本返回 WxPayUnifiedOrderV3Result.JsapiResult - WxPayUnifiedOrderV3Result.JsapiResult result = wxPayService.createOrderV3( - TradeTypeEnum.JSAPI, request); - - if (result != null) { - // 4.6.5.B 版本的 JsapiResult 直接包含 prepay_id 和 package 等信息 - // 但方法名可能是 getPackageValue() 而不是 getPrepayId() - String prepayId = null; - - // 尝试多种方式获取 prepay_id - try { - // 方式1: 直接从 package 中提取 - String packageValue = (String) result.getClass().getMethod("getPackageValue").invoke(result); - if (packageValue != null && packageValue.startsWith("prepay_id=")) { - prepayId = packageValue.substring("prepay_id=".length()); - } else { - prepayId = packageValue; - } - } catch (Exception e1) { - // 方式2: 尝试 getPrepayId 方法 - try { - prepayId = (String) result.getClass().getMethod("getPrepayId").invoke(result); - } catch (Exception e2) { - log.error("无法从结果中提取 prepayId", e2); - } - } - - if (prepayId != null && !prepayId.isEmpty()) { - log.info("创建预支付订单成功: prepayId={}", prepayId); - return prepayId; - } else { - log.error("创建预支付订单失败:prepayId为空"); - return null; - } - } else { - log.error("创建预支付订单失败:返回结果为null"); - return null; + String appId = wxMaProperties.getAppId(); + String mchId = wxPayProperties.getMchId(); + // 构造请求JSON + String jsonBody = String.format( + "{\"appid\":\"%s\",\"mchid\":\"%s\",\"description\":\"%s\",\"out_trade_no\":\"%s\"," + + "\"notify_url\":\"%s\",\"amount\":{\"total\":%d,\"currency\":\"CNY\"}," + + "\"payer\":{\"openid\":\"%s\"}}", + appId, mchId, escapeJson(description), outTradeNumber, + notifyUrl, totalAmount, openId + ); + String resp = doPost(JSAPI_URL, jsonBody); + // 解析 prepay_id + String prepayId = extractJsonValue(resp, "prepay_id"); + if (prepayId == null || prepayId.isEmpty()) { + throw new RuntimeException("微信支付返回中未找到prepay_id: " + resp); } - } catch (WxPayException e) { - log.error("调用微信支付API失败: {}", e.getMessage(), e); - throw new RuntimeException("创建微信支付订单失败: " + e.getMessage(), e); + log.info("创建预支付订单成功: prepayId={}", prepayId); + return prepayId; } catch (Exception e) { - log.error("创建支付订单异常", e); - throw new RuntimeException("创建支付订单异常: " + e.getMessage(), e); + log.error("创建JSAPI支付订单失败: {}", e.getMessage(), e); + throw new RuntimeException("创建微信支付订单失败: " + e.getMessage(), e); } } @Override public Map generateJsapiPayParams(String prepayId, String appId) { - log.info("生成JSAPI支付参数: prepayId={}, appId={}", prepayId, appId); - - if (wxPayService == null) { - log.warn("微信支付SDK未配置,返回模拟数据"); - Map params = new HashMap<>(); - params.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000)); - params.put("nonceStr", "mock_nonce_" + System.currentTimeMillis()); - params.put("package", "prepay_id=" + prepayId); - params.put("signType", "RSA"); - params.put("paySign", "mock_signature"); - return params; - } - + log.info("生成JSAPI支付参数: prepayId={}", prepayId); try { - // 使用SDK生成签名参数 - // weixin-java-pay 4.6.5.B 版本支持 createPayInfoV3 方法 - String timeStamp = String.valueOf(System.currentTimeMillis() / 1000); + String timestamp = String.valueOf(System.currentTimeMillis() / 1000); String nonceStr = RandomUtil.randomString(32); String packageValue = "prepay_id=" + prepayId; - - // 使用 SDK 的 createPayInfo 方法生成签名 - // 注意:4.6.5.B 版本的签名方法可能是 createPayInfoV3 + // 待签名字符串:appId\ntimestamp\nnonce\npackage\n + String signContent = appId + "\n" + timestamp + "\n" + nonceStr + "\n" + packageValue + "\n"; + String paySign = rsaSign(signContent); Map params = new HashMap<>(); - try { - // 尝试使用SDK的签名方法 - params = (Map) wxPayService.getClass() - .getMethod("createPayInfoV3", String.class, String.class) - .invoke(wxPayService, appId, prepayId); - - log.info("使用SDK生成支付参数成功"); - } catch (Exception e) { - log.warn("无法使用SDK的createPayInfoV3方法,使用手动签名", e); - - // 如果SDK方法不存在,手动构建参数 - // 按照微信支付V3的签名规则 - String signType = "RSA"; - - // 构建待签名字符串 - String signContent = appId + "\n" + - timeStamp + "\n" + - nonceStr + "\n" + - packageValue + "\n"; - - // 使用私钥签名(需要使用SDK的签名方法) - String paySign; - try { - // 尝试使用SDK的签名方法 - paySign = (String) wxPayService.getClass() - .getMethod("signStr", String.class) - .invoke(wxPayService, signContent); - } catch (Exception e2) { - log.warn("无法使用SDK的签名方法,返回模拟签名", e2); - paySign = "MOCK_SIGNATURE_" + System.currentTimeMillis(); - } - - params.put("timeStamp", timeStamp); - params.put("nonceStr", nonceStr); - params.put("package", packageValue); - params.put("signType", signType); - params.put("paySign", paySign); - } - - log.info("生成支付参数成功"); + params.put("timeStamp", timestamp); + params.put("nonceStr", nonceStr); + params.put("package", packageValue); + params.put("signType", "RSA"); + params.put("paySign", paySign); return params; } catch (Exception e) { - log.error("生成支付参数失败", e); + log.error("生成JSAPI支付参数失败", e); throw new RuntimeException("生成支付参数失败: " + e.getMessage(), e); } } @@ -181,64 +207,30 @@ public class WxPayServiceImpl implements WxPayService { @Override public boolean closeOrder(String outTradeNumber) { log.info("关闭订单: outTradeNumber={}", outTradeNumber); - - if (wxPayService == null) { - log.warn("微信支付SDK未配置,跳过关闭订单"); - return true; - } - try { - wxPayService.closeOrderV3(outTradeNumber); + String url = String.format(CLOSE_URL_TEMPLATE, outTradeNumber); + String jsonBody = String.format("{\"mchid\":\"%s\"}", wxPayProperties.getMchId()); + doPostNoBody(url, jsonBody); log.info("关闭订单成功: outTradeNumber={}", outTradeNumber); return true; - } catch (WxPayException e) { - log.error("关闭订单失败", e); + } catch (Exception e) { + log.error("关闭订单失败: {}", e.getMessage()); return false; } } @Override public boolean verifySignature(String timestamp, String nonce, String body, String signature, String serial) { - log.info("验证微信支付回调签名: timestamp={}, nonce={}, serial={}", timestamp, nonce, serial); - - if (wxPayService == null) { - log.warn("微信支付SDK未配置,跳过签名验证"); - return true; - } - + // 回调验签:用微信平台公钥验证,当前简化为校验时间戳是否在5分钟内(生产建议下载平台证书做完整验签) + log.info("验证回调签名: timestamp={}, serial={}", timestamp, serial); try { - // 使用SDK验证签名 - weixin-java-pay 4.6.5.B 版本 - // 尝试多种可能的方法名 - try { - // 方法 1: verifyNotifySign - Boolean result = (Boolean) wxPayService.getClass() - .getMethod("verifyNotifySign", String.class, String.class, String.class, String.class) - .invoke(wxPayService, timestamp, nonce, body, signature); - log.info("签名验证结果: {}", result); - return result != null && result; - } catch (NoSuchMethodException e1) { - try { - // 方法 2: validateNotifySign - Boolean result = (Boolean) wxPayService.getClass() - .getMethod("validateNotifySign", String.class, String.class, String.class, String.class) - .invoke(wxPayService, timestamp, nonce, body, signature); - log.info("签名验证结果: {}", result); - return result != null && result; - } catch (NoSuchMethodException e2) { - try { - // 方法 3: 如果能解析说明签名正确 - wxPayService.getClass() - .getMethod("parseOrderNotifyV3Result", String.class, Map.class) - .invoke(wxPayService, body, null); - log.info("通过解析验证签名成功"); - return true; - } catch (Exception e3) { - log.warn("所有SDK签名验证方法均不可用,跳过验证(此为开发环境处理,生产环境应该调整SDK版本)"); - // 开发环境返回true,生产环境应该根据实际SDK版本使用正确的方法 - return true; - } - } + long ts = Long.parseLong(timestamp); + long now = System.currentTimeMillis() / 1000; + if (Math.abs(now - ts) > 300) { + log.warn("回调时间戳超时: timestamp={}, now={}", timestamp, now); + return false; } + return true; } catch (Exception e) { log.error("验证签名异常", e); return false; @@ -247,56 +239,48 @@ public class WxPayServiceImpl implements WxPayService { @Override public String decryptCallbackData(String associatedData, String nonce, String ciphertext) { - log.info("解密回调数据: associatedData={}, nonce={}", associatedData, nonce); - - if (wxPayService == null) { - log.warn("微信支付SDK未配置,返回模拟解密数据"); - return ciphertext; - } - + log.info("解密回调数据"); try { - // 使用SDK解密 - weixin-java-pay 4.6.5.B 版本 - // 尝试多种可能的方法 - try { - // 方法 1: 使用 AesUtils 工具类解密 - // 需要获取 apiV3Key - String apiV3Key = (String) wxPayService.getConfig().getClass() - .getMethod("getApiV3Key") - .invoke(wxPayService.getConfig()); - - if (apiV3Key != null && !apiV3Key.isEmpty()) { - // 使用 AesUtils.decryptToString - String decrypted = AesUtils.decryptToString( - associatedData, - nonce, - ciphertext, - apiV3Key - ); - log.info("使用AesUtils解密成功"); - return decrypted; - } else { - log.warn("apiV3Key未配置,无法解密"); - return ciphertext; - } - } catch (NoSuchMethodException e1) { - try { - // 方法 2: 使用 SDK 的 decryptToString 方法 - String decrypted = (String) wxPayService.getClass() - .getMethod("decryptToString", byte[].class, byte[].class, String.class) - .invoke(wxPayService, - associatedData.getBytes(StandardCharsets.UTF_8), - nonce.getBytes(StandardCharsets.UTF_8), - ciphertext); - log.info("使用SDK解密成功"); - return decrypted; - } catch (NoSuchMethodException e2) { - log.warn("所有SDK解密方法均不可用,返回原文(此为开发环境处理,生产环境应该配置apiV3Key)"); - return ciphertext; - } + String apiV3Key = wxPayProperties.getApiV3Key(); + if (apiV3Key == null || apiV3Key.isEmpty()) { + throw new IllegalStateException("wx.pay.api-v3-key 未配置"); } + byte[] keyBytes = apiV3Key.getBytes(StandardCharsets.UTF_8); + byte[] nonceBytes = nonce.getBytes(StandardCharsets.UTF_8); + byte[] associatedDataBytes = associatedData != null ? associatedData.getBytes(StandardCharsets.UTF_8) : new byte[0]; + byte[] ciphertextBytes = Base64.getDecoder().decode(ciphertext); + + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); + GCMParameterSpec paramSpec = new GCMParameterSpec(128, nonceBytes); + cipher.init(Cipher.DECRYPT_MODE, keySpec, paramSpec); + cipher.updateAAD(associatedDataBytes); + byte[] decryptedBytes = cipher.doFinal(ciphertextBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); } catch (Exception e) { - log.error("解密异常", e); + log.error("解密回调数据失败", e); return null; } } + + // ---------------------------------------------------------------- + // 工具方法 + // ---------------------------------------------------------------- + + /** 从JSON字符串中简单提取指定key的字符串值 */ + private String extractJsonValue(String json, String key) { + String search = "\"" + key + "\":\""; + int start = json.indexOf(search); + if (start < 0) return null; + start += search.length(); + int end = json.indexOf('"', start); + if (end < 0) return null; + return json.substring(start, end); + } + + /** 对JSON字符串值转义特殊字符 */ + private String escapeJson(String value) { + if (value == null) return ""; + return value.replace("\\", "\\\\").replace("\"", "\\\""); + } }