微信支付成功后,异步通知 NotificationParser.parse 解密数据异常
pyrange opened this issue · comments
错误描述
微信支付成功,异步通知解密数据异常
运行环境
java:JDK17
依赖微信sdk:wechatpay-java;0.2.4版本
示例代码
// 使用自动更新平台证书的RSA配置
// 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错
RSAAutoCertificateConfig config = new RSAAutoCertificateConfig.Builder()
.merchantId(WeiXinPayConstant.MCH_ID)
.privateKey(WeiXinPayConstant.MCH_PRIVATE_KEY)
.merchantSerialNumber(WeiXinPayConstant.MCH_SERIAL_NO)
.apiV3Key(WeiXinPayConstant.API_V3_KEY)
.build();
异常信息:
com.wechat.pay.java.core.exception.ValidationException: Processing WechatPay notification,signature verification failed,signType[WECHATPAY2-SHA256-RSA2048] serial[72A58B94F5350B13A124585D15BF88C1E55AB350] message[1672906824
WYDi5b6dyyi36Cry6Vd95cYW5EOrwwNo
{"summary":"支付成功","event_type":"TRANSACTION.SUCCESS","create_time":"2023-01-05T16:16:20+08:00","resource":{"original_type":"transaction","algorithm":"AEAD_AES_256_GCM","ciphertext":"dFZoL6Qa18H3/R0fpBvviskQ1xVaXQDkRSJETChAtBcYfmNvBOqmipI8u1yTTl5ddgjarOdAuK5npSjMjfc1/OEPaNx91R2kG82e3NFaXP3csvtSLCNRN6h1MFBlMPqHVGk8SoOKqgGQAxAmzeLUzUlRI9gQm8GDRvJYzsIkTdWs//2yZZAdEOLXe29Uu+Zj41Te+/iHR0JkmHup9XXMSOZ+cjwmoTlycYd9b0u7j1O4f54fG78jsP76jt6YhniJPhi1nN9Mb2v4wYzH6LHA3wWN4oPLQmXMORL8F+gqm88yyrf79XlcZgovTXM1cUIklDb0pGyennQXSfrzz7Z8dxPmmiLo8zp8XHMUtsb81W8lV6oC4c/nykd7j6NPoI8F6VUYDptCH6aZu/nvsMyCRRvalSFHvLYwIIRtNc1/VB49KKF1H4EncY/lx3gOdI4FSrt8Q/wYt+QPqZ/1QEs0QlFYW98TlXPtCC9pgveR4gZRUHdrvYwxwJT2Gn5rQn18vhZDfxrFaBZLxLYruNyyGRXkBMPqsa9i0Exu6e/gcEcvylo9yQUoJ1GDliOIBJ8eF9rELEos","associated_data":"transaction","nonce":"Zs7DNh4Njdvf"},"resource_type":"encrypt-resource","id":"a179bbb2-257f-5c07-826a-287d39b995f6"}
] sign[hpq6R1V4LO25TYN97VnhkPrRheXyn7WrtmYDLkeI4eF7O248YXYJC6TLCp/T57lNZBAyPtHz+4K0Rm7SqXAQzmGh/OvlAi3FTvXk/Wm8FOp59VqOerxw7HYD3UFItAS0rpozoIC5ha5b2FHqmGCp5l7OGIXLf9PnGW2s551im2BkBLb+1WW2f6udJywoGL/YGVZ4Jl7bwMFYf64GZAc0AIGQqRWBrL7zIiL8Nw4ajBZTiwDOlZiSJg9vzVaHJyj4nSKe8wPy5ZcPq2Hd4JZ0VUvakhiqyxjsz4KJE8nB8HQicMgBSZpuJPRPzxaWd/DB9x73XmCY2A26DRFfs0mZNA==]
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.pyrange.app.main.web.service.payment.weixin.WeixinPaymentServiceImpl.paymentSuccessNotify(WeixinPaymentServiceImpl.java:367)
at com.pyrange.app.main.web.controller.WeixinPaymentController.paymentSuccessNotify(WeixinPaymentController.java:37)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)
at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:57)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:173)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:58)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:173)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:752)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:703)
at com.pyrange.app.main.web.controller.WeixinPaymentController$$SpringCGLIB$$0.paymentSuccessNotify(<generated>)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:207)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:152)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:884)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1080)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:973)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1003)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:906)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:731)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:880)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:814)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:223)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:400)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1739)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:833)
重现bug的步骤
- 微信小程序支付
- 收到微信通知
预期行为
正常解析密文
导致错误的代码片段
Transaction parse = parser.parse(requestParam, Transaction.class);
操作系统
win10
Java 版本
java 17
wechatpay-java 版本
0.2.4
其他信息
无
body要使用原始的报文,而不是 toJSONString()
的字符串。经过序列化和反序列化之后,跟原文不一致。
public ResponseEntity<WxResultInfoVo> wxNotify(
@RequestHeader("wechatpay-serial") String serialNumber,
@RequestHeader("wechatpay-nonce") String nonce,
@RequestHeader("wechatpay-signature") String signature,
@RequestHeader("wechatpay-timestamp") String timestamp,
@RequestHeader("wechatpay-signature-type") String signType,
HttpServletRequest request
) {
String body = IOUtils.toString(request.getInputStream());
RequestParam requestParam = new RequestParam.Builder()
.serialNumber(serialNumber.trim())
.nonce(nonce.trim())
.signature(signature.trim())
.timestamp(timestamp.trim())
// 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
.signType(signType.trim())
.body(body)
.build();
NotificationParser parser = new NotificationParser(config);
Map map = parser.parse(requestParam, Map.class);
这样子也是验签不通过
能给一个获取原始body的例子么?
private String getRequestBody(HttpServletRequest request) {
ByteArrayOutputStream body = new ByteArrayOutputStream();
try {
ServletInputStream inputStream = request.getInputStream();
byte[] buffer = new byte[1024];
for (int length; (length = inputStream.read(buffer)) != -1; ) {
body.write(buffer, 0, length);
}
} catch (IOException ex) {
log.error("支付回调,读取数据流异常", ex);
}
log.info("支付回调,通知消息体:{}", body);
return body.toString();
}
有的极少订单回调会报这个解签错误,大部分订单可以成功,这个是body读取的代码示例,不清楚为什么会这样,微信sdk版本是0.2.7
感谢大佬们先踩坑了,让咱吃了现成的了
private String getRequestBody(HttpServletRequest request) { ByteArrayOutputStream body = new ByteArrayOutputStream(); try { ServletInputStream inputStream = request.getInputStream(); byte[] buffer = new byte[1024]; for (int length; (length = inputStream.read(buffer)) != -1; ) { body.write(buffer, 0, length); } } catch (IOException ex) { log.error("支付回调,读取数据流异常", ex); } log.info("支付回调,通知消息体:{}", body); return body.toString(); }
有的极少订单回调会报这个解签错误,大部分订单可以成功,这个是body读取的代码示例,不清楚为什么会这样,微信sdk版本是0.2.7
我猜你要找的是‘签名探测流量’,https://pay.weixin.qq.com/docs/merchant/development/interface-rules/signature-verification.html