收藏
回答

商家转账用户确认收款模式,回调通知进行验签的时候验签失败,麻烦哪位大神看看,实在是没看出是哪里的问题

打印的日志:

2025-04-17 01:37:21.200 [http-nio-54321-exec-3] INFO  c.i.wechat.web.QyWechatController - >>>wechat notify begin.requestBody is:{"summary":"商家转账单据终态通知","event_type":"MCHTRANSFER.BILL.FINISHED","create_time":"2025-04-17T01:36:30+08:00","resource":{"associated_data":"mch_payment","ciphertext":"aBguEt3BZGcjMHBFhsBoJagx/O8enipdhRr+EHJ2App8Vyw4XnRcaXrOA/Gt64J5G81cCTx5+huOXUlbUGxDSEfwOaU4nMUiPwDY6M7BYbJJtGmjCoID3daIjJK3W+XeHcbrKVd1BADR0vzQ8/fwLIP77HS/hEh5XMyGmBU+vCWgbZcWmST2rudiUeYK2hBgqXeKvbFj+OOPLB9zJGeW9dgZ42g6SH9uwynzLjzRMBn7b5VnGV9q0DtYI2mEgo1eI3EtLIixyocuIIsnUZguX2wjXSiFz4SOjKKafVWZSPLdiYlaulhO8o84au9H8Gn7tWinREixiIH2l93ZmTKq44xqMlrgq1FDrOLd89XACN9mZE5kdTVPvHm5mA3o5bLk80NftZ8iftspgFGFvua7PWhuc/GallL7Yi/sR/3oYg4kXatW3HdYSw==","original_type":"mch_payment","nonce":"yexRCij6Nxti","algorithm":"AEAD_AES_256_GCM"},"resource_type":"encrypt-resource","id":"aff21d5f-654d-597d-a132-cb914ebc6a42"}

2025-04-17 01:37:21.200 [http-nio-54321-exec-3] INFO  c.i.wechat.web.QyWechatController - >>>wechatSignature is:W2N/w3OT/bKSl+LXFnr1WurdhtCgo+JxxiZ5rU0ncnT8A6fw2Wp0oy0cjTc2nZ2LVjwLDKAD6Dmua+iCvxC75ZNFN0BHji9R6hrzujvnxPe6us34zv9MZ7XeL4hilMOeIr+fSPn+96jSF78+xFQqUkX7gKk/Jto3hTYzjnR+MqIJVY61Ej0EVpUsEp/++QvXKPl3nr+iniOHwMVXm2Xoizf6jp51NpBxVOgRQZh8D07iEIhMNPqyK3aYtEngfMYeVMOn7qBYwjtIciLY2TPIPE58g84gvXHAtdwXWl6YS9QLkp2ruF0DT06HCxbFGhBNWXnhtAqXv1jApKrfc/gkvg==

2025-04-17 01:37:21.200 [http-nio-54321-exec-3] INFO  c.i.wechat.web.QyWechatController - >>>wechatPaySerial is:3B52CC8B8323F3BAC723B40EF18AFD9E13D4DD3C

2025-04-17 01:37:21.200 [http-nio-54321-exec-3] INFO  c.i.wechat.web.QyWechatController - >>>wechatpayNonce is:oEBAPZvavTqKNYhvT8TrwZCtIvaCYhCh

2025-04-17 01:37:21.200 [http-nio-54321-exec-3] INFO  c.i.wechat.web.QyWechatController - >>>wechatTimestamp is:1744825041

2025-04-17 01:37:21.200 [http-nio-54321-exec-3] ERROR c.i.wechat.web.QyWechatController - sign verification failed

com.wechat.pay.java.core.exception.ValidationException: Processing WechatPay notification,signature verification failed,signType[WECHATPAY2-SHA256-RSA2048] serial[3B52CC8B8323F3BAC723B40EF18AFD9E13D4DD3C] message[1744825041

oEBAPZvavTqKNYhvT8TrwZCtIvaCYhCh

{"summary":"商家转账单据终态通知","event_type":"MCHTRANSFER.BILL.FINISHED","create_time":"2025-04-17T01:36:30+08:00","resource":{"associated_data":"mch_payment","ciphertext":"aBguEt3BZGcjMHBFhsBoJagx/O8enipdhRr+EHJ2App8Vyw4XnRcaXrOA/Gt64J5G81cCTx5+huOXUlbUGxDSEfwOaU4nMUiPwDY6M7BYbJJtGmjCoID3daIjJK3W+XeHcbrKVd1BADR0vzQ8/fwLIP77HS/hEh5XMyGmBU+vCWgbZcWmST2rudiUeYK2hBgqXeKvbFj+OOPLB9zJGeW9dgZ42g6SH9uwynzLjzRMBn7b5VnGV9q0DtYI2mEgo1eI3EtLIixyocuIIsnUZguX2wjXSiFz4SOjKKafVWZSPLdiYlaulhO8o84au9H8Gn7tWinREixiIH2l93ZmTKq44xqMlrgq1FDrOLd89XACN9mZE5kdTVPvHm5mA3o5bLk80NftZ8iftspgFGFvua7PWhuc/GallL7Yi/sR/3oYg4kXatW3HdYSw==","original_type":"mch_payment","nonce":"yexRCij6Nxti","algorithm":"AEAD_AES_256_GCM"},"resource_type":"encrypt-resource","id":"aff21d5f-654d-597d-a132-cb914ebc6a42"}

] sign[W2N/w3OT/bKSl+LXFnr1WurdhtCgo+JxxiZ5rU0ncnT8A6fw2Wp0oy0cjTc2nZ2LVjwLDKAD6Dmua+iCvxC75ZNFN0BHji9R6hrzujvnxPe6us34zv9MZ7XeL4hilMOeIr+fSPn+96jSF78+xFQqUkX7gKk/Jto3hTYzjnR+MqIJVY61Ej0EVpUsEp/++QvXKPl3nr+iniOHwMVXm2Xoizf6jp51NpBxVOgRQZh8D07iEIhMNPqyK3aYtEngfMYeVMOn7qBYwjtIciLY2TPIPE58g84gvXHAtdwXWl6YS9QLkp2ruF0DT06HCxbFGhBNWXnhtAqXv1jApKrfc/gkvg==]

at com.wechat.pay.java.core.notification.NotificationParser.validateRequest(NotificationParser.java:93)

at com.wechat.pay.java.core.notification.NotificationParser.parse(NotificationParser.java:49)

at com.infosky.wechat.web.MchTransferNotifyController.notifyHandle(MchTransferNotifyController.java:141)

at sun.reflect.GeneratedMethodAccessor543.invoke(Unknown Source)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)

at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)

at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)

at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879)

at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)

at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)

at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)

at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)

at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)

at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)

at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)

=========================================代码=============================================

    @PostMapping("/transfer")
    public ResponseEntity notifyHandle(HttpServletRequest request) throws Exception {
        BufferedReader reader = request.getReader();
        String line;
        StringBuilder sb = new StringBuilder();
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        String myRequestBody = sb.toString();

        logger.info(">>>wechat notify begin.requestBody is:" + myRequestBody );

        String wechatSignature = request.getHeader("Wechatpay-Signature");
        String wechatPaySerial = request.getHeader("Wechatpay-Serial");
        String wechatpayNonce = request.getHeader("Wechatpay-Nonce");
        String wechatTimestamp = request.getHeader("Wechatpay-Timestamp");
        String wechatSignatureType = request.getHeader("Wechatpay-Signature-Type");

        logger.info(">>>wechatSignature is:" + wechatSignature);
        logger.info(">>>wechatPaySerial is:" + wechatPaySerial);
        logger.info(">>>wechatpayNonce is:" + wechatpayNonce);
        logger.info(">>>wechatTimestamp is:" + wechatTimestamp);

        String wechatpaySerialNumber = notificationConfig.createVerifier().getSerialNumber();
        logger.info("notificationConfig`s wechatpaySerialNumber is [{}] ", wechatpaySerialNumber);
        logger.info("??? wechatPaySerial == wechatpaySerialNumber [{}] ", wechatPaySerial.equals(wechatpaySerialNumber));

        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatPaySerial)
                .nonce(wechatpayNonce)
                .signature(wechatSignature)
                .timestamp(wechatTimestamp)
                .body(myRequestBody)
                .signType(wechatSignatureType)
                .build();



        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(notificationConfig);

        try {
            // 以支付通知回调为例,验签、解密并转换成 Transaction
            Notification notification = parser.parse(requestParam, Notification.class);
            logger.info(">>>notification is:" + notification);
            String decryptString = new AesUtil(apiV3Key.getBytes()).decryptToString(notification.getResource().getAssociatedData().getBytes(),
                    notification.getResource().getNonce().getBytes(), notification.getResource().getCiphertext());
            logger.info(">>>decryptString is:" + decryptString);
            TransferBillsDecryptEntity decryptEntity = JSON.parseObject(decryptString, new TypeReference<TransferBillsDecryptEntity>() {
            });
            logger.info(">>>decryptEntity is:" + decryptEntity);
            //TODO 回调业务逻辑处理 ...

        } catch (ValidationException e) {
            // 签名验证失败,返回 401 UNAUTHORIZED 状态码
            logger.error("sign verification failed", e);
            return new ResponseEntity<String>("{\"code\": \"SUCCESS\",\"message\": \"sign verification failed\"}", HttpStatus.UNAUTHORIZED);
        } catch (Exception e) {
            // 本地业务逻辑错误 或 解密失败
            logger.error("ciphertext decrypt failed or other err:", e);
            return new ResponseEntity<String>("{\"code\": \"SUCCESS\",\"message\": \"" + e.getMessage() + "\"}", HttpStatus.INTERNAL_SERVER_ERROR);
        }

        // 处理成功,返回 200 OK 状态码
        return new ResponseEntity<String>("", HttpStatus.OK);
    }

====================================配置类===========================================================

@Configuration
public class MchTransferConfig {
    private static final Logger logger = LoggerFactory.getLogger(QyWechatController.class);
    /**
     * 商户号
     */
    public static String merchantId = PropertiesConfig.readValue("merchantId");

    /**
     * 商户API私钥路径
     */
    public static String privateKeyPath = PropertiesConfig.readValue("privateKeyPath");

    /**
     * 商户证书序列号
     */
    public static String merchantSerialNumber = PropertiesConfig.readValue("merchantSerialNumber");

    /**
     * 商户APIV3密钥
     */
    public static String apiV3Key = PropertiesConfig.readValue("apiV3Key");

    /**
     * 平台证书路径
     */
//    public static String wechatPayCertificatePath = PropertiesConfig.readValue("wechatPayCertificatePath");

    /**
     * 通知配置
     *
     * @return
     */
    @Bean
    public NotificationConfig notificationConfig() {
        // 自动下载平台证书
        return new MyRSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                .privateKeyFromPath(privateKeyPath)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .build();
    }

    /**
     * 商家转账配置
     *
     * @return
     */
    @Bean
    public Config config() {
        // 自动下载平台证书
        return new MyRSAAutoCertificateConfig.Builder()
                .merchantId(merchantId)
                // 使用 com.wechat.pay.java.core.util
                // 中的函数从本地文件中加载商户私钥,商户私钥会用来生成请求的签名
                .privateKeyFromPath(privateKeyPath)
                .merchantSerialNumber(merchantSerialNumber)
                .apiV3Key(apiV3Key)
                .build();
    }
}


回答关注问题邀请回答
收藏

2 个回答

  • Memory (私信不回复)
    Memory (私信不回复)
    星期四 15:21

    你手动能不能验证通过?

    星期四 15:21
    有用
    回复
  • drop
    drop
    星期四 09:53

    实际收到的body字符串得是这个格式(你的参数顺序不是这个),你收到后肯定是自己框架修改了,要用实际收到的去验签才能过

    星期四 09:53
    有用
    回复
登录 后发表内容