22
33import com .wechat .pay .contrib .apache .httpclient .Validator ;
44import java .io .IOException ;
5+ import java .time .DateTimeException ;
6+ import java .time .Duration ;
7+ import java .time .Instant ;
58import org .apache .http .Header ;
69import org .apache .http .HttpEntity ;
710import org .apache .http .client .methods .CloseableHttpResponse ;
@@ -19,24 +22,72 @@ public WechatPay2Validator(Verifier verifier) {
1922 this .verifier = verifier ;
2023 }
2124
25+ static RuntimeException parameterError (String message , Object ... args ) {
26+ message = String .format (message , args );
27+ return new IllegalArgumentException ("parameter error: " + message );
28+ }
29+
30+ static RuntimeException verifyFail (String message , Object ... args ) {
31+ message = String .format (message , args );
32+ return new IllegalArgumentException ("signature verify fail: " + message );
33+ }
34+
2235 @ Override
2336 public final boolean validate (CloseableHttpResponse response ) throws IOException {
24- Header serialNo = response .getFirstHeader ("Wechatpay-Serial" );
25- Header sign = response .getFirstHeader ("Wechatpay-Signature" );
26- Header timestamp = response .getFirstHeader ("Wechatpay-TimeStamp" );
27- Header nonce = response .getFirstHeader ("Wechatpay-Nonce" );
37+ try {
38+ validateParameters (response );
39+
40+ String message = buildMessage (response );
41+ String serial = response .getFirstHeader ("Wechatpay-Serial" ).getValue ();
42+ String signature = response .getFirstHeader ("Wechatpay-Signature" ).getValue ();
2843
29- // todo: check timestamp
30- if (timestamp == null || nonce == null || serialNo == null || sign == null ) {
44+ if (!verifier .verify (serial , message .getBytes ("utf-8" ), signature )) {
45+ throw verifyFail ("serial=[%s] message=[%s] sign=[%s], request-id=[%s]" ,
46+ serial , message , signature ,
47+ response .getFirstHeader ("Request-ID" ).getValue ());
48+ }
49+ } catch (IllegalArgumentException e ) {
50+ log .warn (e .getMessage ());
3151 return false ;
3252 }
3353
34- String message = buildMessage (response );
35- return verifier .verify (serialNo .getValue (), message .getBytes ("utf-8" ), sign .getValue ());
54+ return true ;
55+ }
56+
57+ protected final void validateParameters (CloseableHttpResponse response ) {
58+ String requestId ;
59+ if (!response .containsHeader ("Request-ID" )) {
60+ throw parameterError ("empty Request-ID" );
61+ } else {
62+ requestId = response .getFirstHeader ("Request-ID" ).getValue ();
63+ }
64+
65+ if (!response .containsHeader ("Wechatpay-Serial" )) {
66+ throw parameterError ("empty Wechatpay-Serial, request-id=[%s]" , requestId );
67+ } else if (!response .containsHeader ("Wechatpay-Signature" )){
68+ throw parameterError ("empty Wechatpay-Signature, request-id=[%s]" , requestId );
69+ } else if (!response .containsHeader ("Wechatpay-Timestamp" )) {
70+ throw parameterError ("empty Wechatpay-Timestamp, request-id=[%s]" , requestId );
71+ } else if (!response .containsHeader ("Wechatpay-Nonce" )) {
72+ throw parameterError ("empty Wechatpay-Nonce, request-id=[%s]" , requestId );
73+ } else {
74+ Header timestamp = response .getFirstHeader ("Wechatpay-Timestamp" );
75+ try {
76+ Instant instant = Instant .ofEpochSecond (Long .parseLong (timestamp .getValue ()));
77+ // 拒绝5分钟之外的应答
78+ if (Duration .between (instant , Instant .now ()).abs ().toMinutes () >= 5 ) {
79+ throw parameterError ("timestamp=[%s] expires, request-id=[%s]" ,
80+ timestamp .getValue (), requestId );
81+ }
82+ } catch (DateTimeException | NumberFormatException e ) {
83+ throw parameterError ("invalid timestamp=[%s], request-id=[%s]" ,
84+ timestamp .getValue (), requestId );
85+ }
86+ }
3687 }
3788
3889 protected final String buildMessage (CloseableHttpResponse response ) throws IOException {
39- String timestamp = response .getFirstHeader ("Wechatpay-TimeStamp " ).getValue ();
90+ String timestamp = response .getFirstHeader ("Wechatpay-Timestamp " ).getValue ();
4091 String nonce = response .getFirstHeader ("Wechatpay-Nonce" ).getValue ();
4192
4293 String body = getResponseBody (response );
0 commit comments