From 6c65f4ca416421d362e5512d5b037a9a618a9c58 Mon Sep 17 00:00:00 2001 From: quyixiao <2621048238@qq.com> Date: Sat, 24 Jan 2026 13:02:55 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vv/AppWeiXinCustomerNotifyController.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java b/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java new file mode 100644 index 0000000..169ebad --- /dev/null +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java @@ -0,0 +1,48 @@ +package com.heyu.api.controller.vv; + + +import com.heyu.api.common.annotation.Describe; +import com.heyu.api.data.dao.vv.VvTradeOrderDao; +import com.heyu.api.jsapi.JsapiPrepay; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Slf4j +@RestController +@RequestMapping("/app/weixin") +public class AppWeiXinCustomerNotifyController { + + + + + // 微信支付APIv3密钥 + @Value("${eb.config.weixin.pay.apiv3key}") + private String apiv3key; + + @Autowired + private VvTradeOrderDao tradeOrderDao; + + @Value("${eb.config.weixin.pay.mchid}") + private String mchid; + + + @Autowired + private JsapiPrepay jsapiPrepay; + + @Describe("微信支付回调") + @PostMapping("/customer/notify") + public String customerNotify(@RequestBody String requestBody) { + + + + return "sucess"; + + } + + +} From 2ae7676ed7d2585a3e9637bcab27691273c993c9 Mon Sep 17 00:00:00 2001 From: quyixiao <2621048238@qq.com> Date: Sat, 24 Jan 2026 13:49:00 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/data/utils/WeChatCallbackUtil.java | 116 +++++++++++++++++ .../vv/AppWeiXinCustomerNotifyController.java | 121 +++++++++++++----- 2 files changed, 208 insertions(+), 29 deletions(-) create mode 100644 api-mapper/src/main/java/com/heyu/api/data/utils/WeChatCallbackUtil.java diff --git a/api-mapper/src/main/java/com/heyu/api/data/utils/WeChatCallbackUtil.java b/api-mapper/src/main/java/com/heyu/api/data/utils/WeChatCallbackUtil.java new file mode 100644 index 0000000..e3bc641 --- /dev/null +++ b/api-mapper/src/main/java/com/heyu/api/data/utils/WeChatCallbackUtil.java @@ -0,0 +1,116 @@ +package com.heyu.api.data.utils; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.util.Arrays; +import java.util.Base64; + +/** + * 微信回调验证和加解密工具类 + */ +public class WeChatCallbackUtil { + + /** + * 验证回调 URL + * + * @param token Token + * @param timestamp 时间戳 + * @param nonce 随机字符串 + * @param msgSignature 消息签名 + * @param echostr 加密的随机字符串 + * @param encodingAESKey EncodingAESKey + * @return 解密后的 echostr + * @throws Exception 验证失败或解密失败 + */ + public static String verifyURL(String token, String timestamp, + String nonce, String msgSignature, + String echostr, String encodingAESKey) throws Exception { + // 1. 验证签名 + String signature = generateSignature(token, timestamp, nonce, echostr); + if (!signature.equals(msgSignature)) { + throw new Exception("签名验证失败"); + } + + // 2. 解密 echostr + String result = decrypt(echostr, encodingAESKey); + return result; + } + + /** + * 生成签名 + */ + public static String generateSignature(String token, String timestamp, + String nonce, String echostr) throws Exception { + // 将 token、timestamp、nonce、echostr 按字典序排序 + String[] arr = {token, timestamp, nonce, echostr}; + Arrays.sort(arr); + + // 拼接字符串 + StringBuilder sb = new StringBuilder(); + for (String s : arr) { + sb.append(s); + } + + // SHA1 加密 + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] digest = md.digest(sb.toString().getBytes(StandardCharsets.UTF_8)); + + // 转换为十六进制字符串 + StringBuilder hexString = new StringBuilder(); + for (byte b : digest) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) { + hexString.append('0'); + } + hexString.append(hex); + } + + return hexString.toString(); + } + + /** + * AES 解密 + */ + public static String decrypt(String encryptedData, String encodingAESKey) throws Exception { + // Base64 解码 + byte[] keyBytes = Base64.getDecoder().decode(encodingAESKey); + byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData); + + // 提取前 16 字节作为 IV + byte[] iv = new byte[16]; + System.arraycopy(encryptedBytes, 0, iv, 0, 16); + + // 提取加密内容 + byte[] ciphertext = new byte[encryptedBytes.length - 16]; + System.arraycopy(encryptedBytes, 16, ciphertext, 0, ciphertext.length); + + // AES-256-CBC 解密 + SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES"); + Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec); + + byte[] decrypted = cipher.doFinal(ciphertext); + + // 去除填充并提取消息内容 + // 前 4 字节是消息长度,最后是随机字符串 + int contentLength = bytesToInt(decrypted, 0); + byte[] content = new byte[contentLength]; + System.arraycopy(decrypted, 4, content, 0, contentLength); + + return new String(content, StandardCharsets.UTF_8); + } + + /** + * 字节数组转整数(大端序) + */ + private static int bytesToInt(byte[] bytes, int offset) { + return ((bytes[offset] & 0xFF) << 24) | + ((bytes[offset + 1] & 0xFF) << 16) | + ((bytes[offset + 2] & 0xFF) << 8) | + (bytes[offset + 3] & 0xFF); + } +} \ No newline at end of file diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java b/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java index 169ebad..2525b28 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java @@ -1,48 +1,111 @@ package com.heyu.api.controller.vv; -import com.heyu.api.common.annotation.Describe; -import com.heyu.api.data.dao.vv.VvTradeOrderDao; -import com.heyu.api.jsapi.JsapiPrepay; +import com.heyu.api.data.utils.WeChatCallbackUtil; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; @Slf4j @RestController -@RequestMapping("/app/weixin") +@RequestMapping("/app/weixin/customer") public class AppWeiXinCustomerNotifyController { - - // 微信支付APIv3密钥 - @Value("${eb.config.weixin.pay.apiv3key}") - private String apiv3key; - - @Autowired - private VvTradeOrderDao tradeOrderDao; - - @Value("${eb.config.weixin.pay.mchid}") - private String mchid; + public static String token = "9Iosbni8qAU8Kcmy8tgRLw"; - @Autowired - private JsapiPrepay jsapiPrepay; - - @Describe("微信支付回调") - @PostMapping("/customer/notify") - public String customerNotify(@RequestBody String requestBody) { + public static String encodingAESKey = "miBhTSRiwCgn9aYoa8d1TDu62GYFV2aE03vXl2Rrgv6"; - - return "sucess"; - + /** + * 验证回调 URL (GET 请求) + */ + @GetMapping("/verify") + public String verify(@RequestParam("msg_signature") String msgSignature, + @RequestParam("timestamp") String timestamp, + @RequestParam("nonce") String nonce, + @RequestParam("echostr") String echostr) { + try { + // 验证并解密 + String result = WeChatCallbackUtil.verifyURL( + token, timestamp, nonce, msgSignature, echostr, encodingAESKey + ); + return result; + } catch (Exception e) { + e.printStackTrace(); + return "验证失败"; + } } + + + /** + * 接收回调消息 (POST 请求) + */ + @PostMapping("/receiveMessage") + public String receiveMessage(@RequestParam("msg_signature") String msgSignature, + @RequestParam("timestamp") String timestamp, + @RequestParam("nonce") String nonce, + HttpServletRequest request) { + try { + // 读取请求体 + String encryptedMessage = getRequestBody(request); + + // 验证签名 + if (!verifySignature(msgSignature, timestamp, nonce, encryptedMessage)) { + return "签名验证失败"; + } + + // 解密消息 + String decryptedMessage = WeChatCallbackUtil.decrypt( + encryptedMessage, encodingAESKey + ); + + // 解析 XML 消息 + // TODO: 解析消息内容并处理 + + // 返回成功响应 + return "success"; + + } catch (Exception e) { + e.printStackTrace(); + return "处理失败"; + } + } + + /** + * 验证签名 + */ + private boolean verifySignature(String msgSignature, String timestamp, + String nonce, String encryptedMessage) { + try { + String signature = WeChatCallbackUtil.generateSignature( + token, timestamp, nonce, encryptedMessage + ); + return signature.equals(msgSignature); + } catch (Exception e) { + return false; + } + } + + /** + * 获取请求体内容 + */ + private String getRequestBody(HttpServletRequest request) throws IOException { + StringBuilder sb = new StringBuilder(); + try (java.io.BufferedReader reader = request.getReader()) { + String line; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + } + return sb.toString(); + } +} + } From 33ecd52c074e2ef66d67f9925fdd582dcaca90af Mon Sep 17 00:00:00 2001 From: quyixiao <2621048238@qq.com> Date: Sat, 24 Jan 2026 13:53:19 +0800 Subject: [PATCH 3/4] jruqwhnt --- .../api/controller/vv/AppWeiXinCustomerNotifyController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java b/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java index 2525b28..09cc5df 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java @@ -108,4 +108,3 @@ public class AppWeiXinCustomerNotifyController { } } -} From a54bc76d3c15281347d00eb7fbd9169a8cc969f7 Mon Sep 17 00:00:00 2001 From: quyixiao <2621048238@qq.com> Date: Sat, 24 Jan 2026 14:07:30 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/com/heyu/api/aop/ZhenZhenLogAop.java | 2 +- .../api/controller/vv/AppWeiXinCustomerNotifyController.java | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/api-web/api-interface/src/main/java/com/heyu/api/aop/ZhenZhenLogAop.java b/api-web/api-interface/src/main/java/com/heyu/api/aop/ZhenZhenLogAop.java index 0d41ead..01d4d04 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/aop/ZhenZhenLogAop.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/aop/ZhenZhenLogAop.java @@ -61,7 +61,7 @@ public class ZhenZhenLogAop { public final static List not_login_urls = Arrays.asList(user_login_url, anonymous_login_url, - "/app/weixin/payNotify","/app/weixin/refundNotify"); + "/app/weixin/payNotify","/app/weixin/refundNotify","/app/weixin/customer/verify","/app/weixin/customer/receiveMessage"); public Map classHasAnnotation = new HashMap(); diff --git a/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java b/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java index 09cc5df..3d28d91 100644 --- a/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java +++ b/api-web/api-interface/src/main/java/com/heyu/api/controller/vv/AppWeiXinCustomerNotifyController.java @@ -24,6 +24,10 @@ public class AppWeiXinCustomerNotifyController { /** * 验证回调 URL (GET 请求) */ + // https://api.1024api.com/api-interface/app/weixin/customer/verify?msg_signature=5392430904602161909×tamp=1737681600&nonce=1234567890&echostr=1234567890 + + // https://api.1024api.com/api-interface/app/weixin/customer/verify + @GetMapping("/verify") public String verify(@RequestParam("msg_signature") String msgSignature, @RequestParam("timestamp") String timestamp, @@ -47,6 +51,7 @@ public class AppWeiXinCustomerNotifyController { /** * 接收回调消息 (POST 请求) */ + // /app/weixin/customer/receiveMessage @PostMapping("/receiveMessage") public String receiveMessage(@RequestParam("msg_signature") String msgSignature, @RequestParam("timestamp") String timestamp,