weixin-java-pay-demo
weixin-java-pay-demo copied to clipboard
支付回调的时候调用parseOrderNotifyV3Result有时候会报这样的错
问题(提问前,请确保阅读过项目首页说明以及SDK wiki文档相关内容)
简要描述
Cannot invoke "com.github.binarywang.wxpay.bean.notify.OriginNotifyResponse.getResource()" because "response" is null| at com.github.binarywang.wxpay.service.impl.BaseWxPayServiceImpl.parseOrderNotifyV3Result(BaseWxPayServiceImpl.java:353)| at com.care.health.pay.recevier.PayNotifyReceiver.notifySuccess(PayNotifyReceiver.java:75)| at jdk.internal.reflect.GeneratedMethodAccessor463.invoke(Unknown Source)| at
版本情况
- WxJava 版本号: 4.4.0
期待结果
实际情况
这里我们是做了个异步事件不知道是不是跟这个关系
重现步骤
- 发起支付
- 支付回调
- 回调的时候有几率会报这样的错
日志
日志内容如果过多,请将日志放在 pastebin 或者其他地方,并将url地址贴在这里
您好!这个错误 Cannot invoke "com.github.binarywang.wxpay.bean.notify.OriginNotifyResponse.getResource()" because "response" is null发生在 BaseWxPayServiceImpl.parseOrderNotifyV3Result 方法的第353行,当尝试调用 response.getResource() 时,发现 response 对象本身是 null。这通常意味着在解析微信支付V3版本的回调通知时,未能成功将回调的原始数据(通常是JSON字符串)反序列化为 OriginNotifyResponse 对象。
根据您提供的信息:
- WxJava 版本号: 4.4.0
- 问题场景: 支付回调时有几率发生。
- 异步事件: 您提到可能与异步事件有关。
可能的原因及排查方向:
-
回调请求体为空或格式不正确:
- 最直接的原因是微信支付发送过来的回调请求的 body (请求体) 可能在某些情况下是空的,或者不是一个合法的JSON字符串,导致
weixin-java-pay库在尝试解析时得到一个null的HttpServletRequest对象或者无法正确读取到请求体内容,进而导致response为null。 - 排查:在您的
PayNotifyReceiver.notifySuccess方法接收到回调的最开始,以及在调用parseOrderNotifyV3Result之前,务必打印接收到的原始回调请求的头部信息和完整的请求体内容。这样可以确认在发生错误时,微信支付实际发送过来的数据是什么。// 在 PayNotifyReceiver.notifySuccess 方法中 // HttpServletRequest request = ...; (获取请求对象) // String requestBody = request.getReader().lines().collect(Collectors.joining(System.lineSeparator())); // log.info("Received WeChat Pay V3 Notify Request Body: " + requestBody); // log.info("Received WeChat Pay V3 Notify Request Headers: " + getAllHeaders(request)); // 一个辅助方法打印所有头部 // ... 然后再调用 wxPayService.parseOrderNotifyV3Result(requestBody, signature, nonce, timestamp, apiV3Key, serialNo) // 注意:parseOrderNotifyV3Result 方法通常需要传入请求体字符串和头部中的签名信息等。 // 您需要确认您是如何将 HttpServletRequest 传递或转换并最终让 BaseWxPayServiceImpl 使用的。 // 在 4.4.0 版本,parseOrderNotifyV3Result 接收的参数可能是 (String xmlData, String signType, String sign) 或类似, // 或者直接是 (HttpServletRequest request, String apiV3Key) 这样的形式,然后内部处理。 // 您需要确认您调用的是哪个重载方法,以及参数是否正确传递。 - 根据
BaseWxPayServiceImpl.java:353这个行号,在weixin-java-pay4.4.0 版本中,parseOrderNotifyV3Result方法内部确实有一个步骤是解析请求体为OriginNotifyResponse。如果传入的请求体字符串本身就有问题,或者解析逻辑出错,response就会是null。
- 最直接的原因是微信支付发送过来的回调请求的 body (请求体) 可能在某些情况下是空的,或者不是一个合法的JSON字符串,导致
-
异步处理导致请求上下文丢失或提前关闭:
- 如果您在异步线程中处理回调,并且直接传递了原始的
HttpServletRequest对象,那么当异步任务执行时,原始的HTTP请求可能已经结束,连接已关闭,导致无法再从request对象中读取请求体。 - 排查/解决:在进入异步处理之前,就应该立即读取并保存完整的请求体字符串。然后将这个字符串传递给异步任务进行处理,而不是传递
HttpServletRequest对象本身。
在// 同步部分 // String requestBody = request.getReader().lines().collect(Collectors.joining(System.lineSeparator())); // String signature = request.getHeader("Wechatpay-Signature"); // String nonce = request.getHeader("Wechatpay-Nonce"); // String timestamp = request.getHeader("Wechatpay-Timestamp"); // String serial = request.getHeader("Wechatpay-Serial"); // eventPublisher.publishEvent(new WeChatPayNotifyEvent(requestBody, signature, nonce, timestamp, serial)); // 异步事件监听器中 // public void handleWeChatPayNotify(WeChatPayNotifyEvent event) { // WxPayNotifyV3Result result = wxPayService.parseOrderNotifyV3Result( // event.getRequestBody(), // event.getSignature(), // event.getNonce(), // event.getTimestamp(), // // this.wxPayConfig.getApiV3Key(), // 从配置获取 // event.getSerial() // ); // // ... // }weixin-java-pay4.4.0 中,parseOrderNotifyV3Result的一个典型重载可能是这样的:public WxPayNotifyV3Result parseOrderNotifyV3Result(String notifyData, String signature, String nonce, String timestamp, String apiV3Key, String serialNo)或者它也可能有一个直接接收HttpServletRequest的版本,内部做了读取。关键是确保读取发生在请求有效的时候。
- 如果您在异步线程中处理回调,并且直接传递了原始的
-
Filter 或 Interceptor 提前消费了请求体:
- 某些框架的 Filter 或 Interceptor (例如日志记录、安全扫描等) 可能会提前读取请求体。标准的
HttpServletRequest的InputStream默认只能读取一次。如果其他组件已经读取了,后续代码再尝试读取就会得到空内容。 - 排查/解决:检查您的项目中是否有其他 Filter 或 Interceptor 在
PayNotifyReceiver之前处理了回调请求。如果有,确保它们使用了HttpServletRequestWrapper之类的机制来缓存请求体,以便后续可以重复读取。或者,在您的第一个处理点(比如一个专门的Filter)就读取并缓存请求体,然后传递缓存的内容。
- 某些框架的 Filter 或 Interceptor (例如日志记录、安全扫描等) 可能会提前读取请求体。标准的
-
微信支付回调的偶发性问题:
- 虽然概率较低,但不能完全排除微信支付在极少数情况下发送了不完整或空的回调。打印原始日志是确认这一点的唯一方法。
-
weixin-java-pay库的特定 Bug (特定于4.4.0版本):- 虽然您没有找到相关的 issue,但如果以上都排查过,可以考虑是否是特定版本下的一个未被广泛发现的 bug。不过通常这种核心解析逻辑会比较稳定。
建议的调试步骤:
-
详细日志记录:在
PayNotifyReceiver.notifySuccess方法的最开始,以及调用parseOrderNotifyV3Result之前,务必记录下所有相关的HTTP头部(特别是微信支付V3回调要求的Wechatpay-Signature,Wechatpay-Nonce,Wechatpay-Timestamp,Wechatpay-Serial)和完整的HTTP请求体。 -
同步转异步的时机:如果您使用了异步事件,请确保在将任务交给异步处理器之前,已经将所有需要的数据(如请求体字符串、头部签名信息)从
HttpServletRequest中提取出来。异步处理器应该处理这些提取出来的数据,而不是原始的HttpServletRequest对象。 -
检查
parseOrderNotifyV3Result的调用方式:确认您是如何调用parseOrderNotifyV3Result的。BaseWxPayServiceImpl中有多个重载版本。- 例如,有一个版本是
parseOrderNotifyV3Result(HttpServletRequest request, String apiV3Key)。如果使用这个版本,库内部会尝试从request读取。 - 还有一个版本是
parseOrderNotifyV3Result(String notifyData, String signature, String nonce, String timestamp, String apiV3Key, String serialNo)。如果使用这个版本,您需要自己先从HttpServletRequest中安全地获取notifyData(请求体字符串) 和其他头部信息。 根据您提供的堆栈信息,错误发生在BaseWxPayServiceImpl.java:353。您需要查看 4.4.0 版本中该行的具体代码,了解它期望response对象是如何被初始化的,以及它的前置依赖是什么。
在
weixin-java-pay4.4.0 的BaseWxPayServiceImpl中,parseOrderNotifyV3Result(HttpServletRequest request, String apiV3Key)方法内部会调用this.parseOrderNotifyV3Result(request, null, null, null, apiV3Key, null);然后这个会进一步调用parseOrderNotifyV3Result(request.getHeader("Wechatpay-Signature"), request.getHeader("Wechatpay-Nonce"), request.getHeader("Wechatpay-Timestamp"), EntityUtils.toString(request.getEntity()), apiV3Key, request.getHeader("Wechatpay-Serial"))(或者类似的读取方式)。关键点在于
EntityUtils.toString(request.getEntity())或request.getReader()这类操作。如果请求体已被消费,这里就会拿到空字符串或null,导致后续WxPayNotifyV3Result.build()方法在尝试解析这个空内容为OriginNotifyResponse时返回null。 - 例如,有一个版本是
总结:
最可能的原因是请求体在传递给 parseOrderNotifyV3Result 方法时已经为空,或者在异步处理中 HttpServletRequest 对象已失效。请重点排查请求体的读取时机和异步处理的数据传递方式。