wechatpay-apiv3 / wechatpay-java

微信支付 APIv3 的官方 Java Library
Apache License 2.0
961 stars 209 forks source link

退款回调解密偶尔异常 #223

Closed xchd closed 1 year ago

xchd commented 1 year ago

错误描述

偶尔会失败。异常信息是:IllegalArgumentException,详细错误日志:Last unit does not have enough valid bits。

重现bug的步骤

无法重现,不是每次回调都会报错

预期行为

退款回调的时候,偶尔会出现解密失败。

导致错误的代码片段

@Autowired
    private RSAAutoCertificateConfig config;  

 @PostMapping(value = "/call")
    public String callBack(@RequestHeader("Wechatpay-Serial") String wechatPayCertificateSerialNumber,
                           @RequestHeader("Wechatpay-Signature") String signature,
                           @RequestHeader("Wechatpay-Timestamp") String timstamp,
                           @RequestHeader("Wechatpay-Nonce") String nonce,
                           @RequestBody String requestBody) {
        log.info("==退款回调:{}",requestBody);
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatPayCertificateSerialNumber)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timstamp)
                // 若未设置signType,默认值为 WECHATPAY2-SHA256-RSA2048
                .body(requestBody)
                .build();

        // 初始化 NotificationParser

        NotificationParser parser = new NotificationParser(config);

        // 验签并解密报文,这里会偶然报错
        JSONObject decryptObject = parser.parse(requestParam, JSONObject.class);
        log.info("==>refund callBack :{}", decryptObject.toJSONString());

操作系统

linux

Java 版本

8

wechatpay-java 版本

0.2.9

其他信息

No response

xy-peng commented 1 year ago

麻烦提供一些更详细的一些日志吧,比如异常堆栈,回调的报文。

xchd commented 1 year ago

麻烦提供一些更详细的一些日志吧,比如异常堆栈,回调的报文。

java.lang.IllegalArgumentException: Last unit does not have enough valid bits at java.util.Base64$Decoder.decode0(Base64.java:734) at java.util.Base64$Decoder.decode(Base64.java:526) at java.util.Base64$Decoder.decode(Base64.java:549) at com.wechat.pay.java.core.cipher.AbstractVerifier.verify(AbstractVerifier.java:39) at com.wechat.pay.java.core.cipher.AbstractVerifier.verify(AbstractVerifier.java:60) at com.wechat.pay.java.core.notification.NotificationParser.validateRequest(NotificationParser.java:90) at com.wechat.pay.java.core.notification.NotificationParser.parse(NotificationParser.java:49) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:752) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:56) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175) at org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor.invoke(AfterReturningAdviceInterceptor.java:55) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175) at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:175) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 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:892) 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: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:652) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:113) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:113) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:124) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:88) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:113) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:97) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:74) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:94) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:114) at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:104) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) 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:343) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:748)

xchd commented 1 year ago

麻烦提供一些更详细的一些日志吧,比如异常堆栈,回调的报文。 回调报文 Wechatpay-Serial: 6CD90D2417DF9DB5561BAB1D6B32B51C58CA4322

Wechatpay-Signature:WECHATPAY/SIGNTEST/05tpYMg4Do1JbOmUyZub2DbcCL0o48W2IBvj4Z74f9AP4El8Md+kBZnSdPsSI8RjyM1FKistWV6ifrizuTOVnpvkT9Y2uzGJ6dzpUzRm9HCA9Dymw7wmQG+iXE8xS1quASJf9UUiWXtgohEA2mxAuTKOpHMabUL1JcuhIGljdVfSwtKCpMU1r00JvtWQOlAggLSy4yx3S7uJqYTd4qlm62yovtTJgENitmxgwRaOMa8rzReiH8XtYsGkcZ3xVgFajN/h3wWqJBTEoF+d+74kUeeNEmVrrQOZE2eYjLMsyz7GLvqtcD1o0t0h4jVv803mZCLefO4v0taQ0g==

Wechatpay-Timestamp: 1692949854

Wechatpay-Nonce: BKGs3aPfo5FLfaPAnZ2LsJ4I6MwOosvQ

requestBody: {"id":"90beb317-a20b-59a2-9fcb-f2f6d031aa47","create_time":"2023-08-25T15:50:55+08:00","resource_type":"encrypt-resource","event_type":"REFUND.SUCCESS","summary":"退款成功","resource":{"original_type":"refund","algorithm":"AEAD_AES_256_GCM","ciphertext":"p3Zf9/XINt374OEQ3z2Rwp5GE9Pml5DhxPBbYYA7pe/jpq3y0viwGyPNgCOQcTovAFJdfPIajldPYj0g5Q5QELo0C7JEslOx/mdUKDxaKCrFDz8UhkOFHbHQcqjEh3nqzh1v1jszSGTeEfbO3Y6OXSVLIHOuZ+63k6b4yr1rZUo4oy90wH90/c+V5ZWNLUF2Vwv9ZhWd9fXv4RoCQCLF+bL8jdvdayjrC8OTquWWgPZWCnnHjtyLp1bDj3K++BITcnKDl8NHbBoGj6RZWxllRbxxvwvaBF8b7mCV6QmJUiNydR2VOuldE3Qo82T69x0GepcR8NN0l0INKwo3NTFPkQZ6nixdfyGOof/3v1Fh4t3+px3Y5wQ9KRpuZ3nNtGInIX09BY68Y0eceGvlkBwaWGzuYmu9g23CvmKwPdtxS4BbgCrL9Rg278kmJQuoRPK96We9IAs1bwvxr/ud7zLvaeW0hMeWYwB74zOij0NmNSC6Axr0f0Bl6T7FBy/GuaHaGZAEELSc1Uv3IaeH2ZqYOQq6MUs=","associated_data":"refund","nonce":"x3TeWYWZ5Ejp"}}

xy-peng commented 1 year ago

Wechatpay-Signature:WECHATPAY/SIGNTEST/05tpYMg4Do1JbOmUyZub2DbcCL0o48W2IBvj4Z74f9AP4El8Md+kBZnSdPsSI8RjyM1FKistWV6ifrizuTOVnpvkT9Y2uzGJ6dzpUzRm9HCA9Dymw7wmQG+iXE8xS1quASJf9UUiWXtgohEA2mxAuTKOpHMabUL1JcuhIGljdVfSwtKCpMU1r00JvtWQOlAggLSy4yx3S7uJqYTd4qlm62yovtTJgENitmxgwRaOMa8rzReiH8XtYsGkcZ3xVgFajN/h3wWqJBTEoF+d+74kUeeNEmVrrQOZE2eYjLMsyz7GLvqtcD1o0t0h4jVv803mZCLefO4v0taQ0g==

你收到的这个是探测请求,可以参考应对签名探测流量

验签返回失败,是正确的。商户服务端返回状态码 400 Invalid Request 就好了,我们会使用正确的签名再次回调商户的。