From 579c7f681a243440aeb8d02b10e68b4685ece666 Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Tue, 18 Aug 2026 12:48:24 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=E6=B6=88=E9=99=A4=E6=97=A7=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E5=A4=B4=E7=A9=BA=E5=80=BC=E9=87=8D=E8=BD=BD=E6=AD=A7?= =?UTF-8?q?=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wxpay/bean/ecommerce/SignatureHeader.java | 2 +- .../LegacyEcommerceApiCompatibilityTest.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java index 9bf268278..fe7eee99b 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java @@ -18,7 +18,7 @@ @NoArgsConstructor @AllArgsConstructor @Deprecated -public class SignatureHeader implements Serializable { +public class SignatureHeader extends com.github.binarywang.wxpay.bean.notify.SignatureHeader implements Serializable { private static final long serialVersionUID = -6958015499416059949L; /** * 时间戳 diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/LegacyEcommerceApiCompatibilityTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/LegacyEcommerceApiCompatibilityTest.java index a82dadb76..1b0e1302d 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/LegacyEcommerceApiCompatibilityTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/LegacyEcommerceApiCompatibilityTest.java @@ -25,4 +25,23 @@ public void shouldKeepLegacyRefundAndWithdrawNotificationSignatures() throws Exc Assert.assertNotNull(EcommerceService.class.getMethod("parseRefundNotifyResult", String.class, legacyHeader)); Assert.assertNotNull(EcommerceService.class.getMethod("parseWithdrawNotifyResult", String.class, legacyHeader)); } + + @Test + public void shouldMakeLegacyHeaderMoreSpecificAndPreserveSignatureFields() { + com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader legacyHeader = + new com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader(); + legacyHeader.setTimeStamp("timestamp"); + legacyHeader.setNonce("nonce"); + legacyHeader.setSigned("signed"); + legacyHeader.setSerialNo("serial-no"); + + Assert.assertTrue(com.github.binarywang.wxpay.bean.notify.SignatureHeader.class + .isAssignableFrom(legacyHeader.getClass())); + com.github.binarywang.wxpay.bean.notify.SignatureHeader unifiedHeader = + EcommerceService.toUnifiedSignatureHeader(legacyHeader); + Assert.assertEquals(unifiedHeader.getTimeStamp(), "timestamp"); + Assert.assertEquals(unifiedHeader.getNonce(), "nonce"); + Assert.assertEquals(unifiedHeader.getSignature(), "signed"); + Assert.assertEquals(unifiedHeader.getSerial(), "serial-no"); + } } From 3d8746e1b61975d7150df8884feb4d6a4ea8265f Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Tue, 18 Aug 2026 12:51:38 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E5=85=BC=E5=AE=B9=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E5=A4=B4=E7=BB=A7=E6=89=BF=E6=9E=84=E5=BB=BA=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wxpay/bean/ecommerce/SignatureHeader.java | 14 ++------------ .../wxpay/bean/notify/SignatureHeader.java | 4 ++-- .../LegacyEcommerceApiCompatibilityTest.java | 11 ++++++----- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java index fe7eee99b..cefac5a9d 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java @@ -1,9 +1,9 @@ package com.github.binarywang.wxpay.bean.ecommerce; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; import java.io.Serializable; @@ -14,22 +14,12 @@ * @author cloudX */ @Data -@Builder +@SuperBuilder @NoArgsConstructor @AllArgsConstructor @Deprecated public class SignatureHeader extends com.github.binarywang.wxpay.bean.notify.SignatureHeader implements Serializable { private static final long serialVersionUID = -6958015499416059949L; - /** - * 时间戳 - */ - private String timeStamp; - - /** - * 随机串 - */ - private String nonce; - /** * 已签名字符串 */ diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/SignatureHeader.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/SignatureHeader.java index cd1fbc42d..1381759fd 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/SignatureHeader.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/notify/SignatureHeader.java @@ -1,9 +1,9 @@ package com.github.binarywang.wxpay.bean.notify; import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; import java.io.Serializable; @@ -14,7 +14,7 @@ * @author thinstar */ @Data -@Builder +@SuperBuilder @NoArgsConstructor @AllArgsConstructor public class SignatureHeader implements Serializable { diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/LegacyEcommerceApiCompatibilityTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/LegacyEcommerceApiCompatibilityTest.java index 1b0e1302d..07030032d 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/LegacyEcommerceApiCompatibilityTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/LegacyEcommerceApiCompatibilityTest.java @@ -29,11 +29,12 @@ public void shouldKeepLegacyRefundAndWithdrawNotificationSignatures() throws Exc @Test public void shouldMakeLegacyHeaderMoreSpecificAndPreserveSignatureFields() { com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader legacyHeader = - new com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader(); - legacyHeader.setTimeStamp("timestamp"); - legacyHeader.setNonce("nonce"); - legacyHeader.setSigned("signed"); - legacyHeader.setSerialNo("serial-no"); + com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader.builder() + .timeStamp("timestamp") + .nonce("nonce") + .signed("signed") + .serialNo("serial-no") + .build(); Assert.assertTrue(com.github.binarywang.wxpay.bean.notify.SignatureHeader.class .isAssignableFrom(legacyHeader.getClass())); From 553ed89556dc5ea464c88e52ad25c7909c946f0a Mon Sep 17 00:00:00 2001 From: Binary Wang Date: Tue, 18 Aug 2026 15:03:33 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=E4=BF=9D=E6=8C=81=E6=97=A7=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E5=A4=B4=E4=BA=8C=E8=BF=9B=E5=88=B6=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wxpay/bean/ecommerce/SignatureHeader.java | 120 ++++++++++++++++-- .../LegacyEcommerceApiCompatibilityTest.java | 45 ++++++- 2 files changed, 155 insertions(+), 10 deletions(-) diff --git a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java index cefac5a9d..cbae86043 100644 --- a/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java +++ b/weixin-java-pay/src/main/java/com/github/binarywang/wxpay/bean/ecommerce/SignatureHeader.java @@ -1,9 +1,9 @@ package com.github.binarywang.wxpay.bean.ecommerce; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import lombok.experimental.SuperBuilder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; import java.io.Serializable; @@ -13,13 +13,14 @@ * * @author cloudX */ -@Data -@SuperBuilder -@NoArgsConstructor -@AllArgsConstructor @Deprecated +@Getter +@Setter +@EqualsAndHashCode(callSuper = false) +@ToString(callSuper = false) public class SignatureHeader extends com.github.binarywang.wxpay.bean.notify.SignatureHeader implements Serializable { private static final long serialVersionUID = -6958015499416059949L; + /** * 已签名字符串 */ @@ -29,4 +30,107 @@ public class SignatureHeader extends com.github.binarywang.wxpay.bean.notify.Sig * 证书序列号 */ private String serialNo; + + /** + * 保留在旧类中的序列化字段,避免升级后反序列化旧数据时丢失。 + */ + private String timeStamp; + + /** + * 保留在旧类中的序列化字段,避免升级后反序列化旧数据时丢失。 + */ + private String nonce; + + public SignatureHeader() { + super(); + } + + /** + * 保留 4.8.4 及以前版本的构造器签名。 + */ + public SignatureHeader(String timeStamp, String nonce, String signed, String serialNo) { + setTimeStamp(timeStamp); + setNonce(nonce); + this.signed = signed; + this.serialNo = serialNo; + } + + private SignatureHeader(SignatureHeaderBuilder builder) { + super(builder); + this.timeStamp = builder.timeStamp; + this.nonce = builder.nonce; + this.signed = builder.signed; + this.serialNo = builder.serialNo; + } + + @Override + public String getTimeStamp() { + return this.timeStamp; + } + + @Override + public void setTimeStamp(String timeStamp) { + super.setTimeStamp(timeStamp); + this.timeStamp = timeStamp; + } + + @Override + public String getNonce() { + return this.nonce; + } + + @Override + public void setNonce(String nonce) { + super.setNonce(nonce); + this.nonce = nonce; + } + + /** + * 保留旧版 builder 的类型和方法返回值描述符。 + */ + public static SignatureHeaderBuilder builder() { + return new SignatureHeaderBuilder(); + } + + public static class SignatureHeaderBuilder extends com.github.binarywang.wxpay.bean.notify.SignatureHeader + .SignatureHeaderBuilder { + private String timeStamp; + private String nonce; + private String signed; + private String serialNo; + + @Override + public SignatureHeaderBuilder timeStamp(String timeStamp) { + super.timeStamp(timeStamp); + this.timeStamp = timeStamp; + return this; + } + + @Override + public SignatureHeaderBuilder nonce(String nonce) { + super.nonce(nonce); + this.nonce = nonce; + return this; + } + + public SignatureHeaderBuilder signed(String signed) { + this.signed = signed; + return this; + } + + public SignatureHeaderBuilder serialNo(String serialNo) { + this.serialNo = serialNo; + return this; + } + + @Override + protected SignatureHeaderBuilder self() { + return this; + } + + @Override + public SignatureHeader build() { + return new SignatureHeader(this); + } + } } diff --git a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/LegacyEcommerceApiCompatibilityTest.java b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/LegacyEcommerceApiCompatibilityTest.java index 07030032d..21f371d12 100644 --- a/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/LegacyEcommerceApiCompatibilityTest.java +++ b/weixin-java-pay/src/test/java/com/github/binarywang/wxpay/service/LegacyEcommerceApiCompatibilityTest.java @@ -5,8 +5,12 @@ import org.testng.Assert; import org.testng.annotations.Test; +import java.io.ByteArrayInputStream; +import java.io.ObjectInputStream; +import java.util.Base64; + /** - * Compile-time compatibility checks for the pre-#4014 e-commerce API. + * Compatibility checks for the pre-#4014 e-commerce API. */ public class LegacyEcommerceApiCompatibilityTest { @@ -27,7 +31,7 @@ public void shouldKeepLegacyRefundAndWithdrawNotificationSignatures() throws Exc } @Test - public void shouldMakeLegacyHeaderMoreSpecificAndPreserveSignatureFields() { + public void shouldKeepLegacySignatureHeaderConstructorAndBuilderAbi() throws Exception { com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader legacyHeader = com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader.builder() .timeStamp("timestamp") @@ -36,6 +40,14 @@ public void shouldMakeLegacyHeaderMoreSpecificAndPreserveSignatureFields() { .serialNo("serial-no") .build(); + Assert.assertNotNull(com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader.class.getConstructor( + String.class, String.class, String.class, String.class)); + Assert.assertEquals(com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader.SignatureHeaderBuilder.class, + com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader.SignatureHeaderBuilder.class + .getMethod("timeStamp", String.class).getReturnType()); + Assert.assertEquals(com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader.SignatureHeaderBuilder.class, + com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader.SignatureHeaderBuilder.class + .getMethod("nonce", String.class).getReturnType()); Assert.assertTrue(com.github.binarywang.wxpay.bean.notify.SignatureHeader.class .isAssignableFrom(legacyHeader.getClass())); com.github.binarywang.wxpay.bean.notify.SignatureHeader unifiedHeader = @@ -45,4 +57,33 @@ public void shouldMakeLegacyHeaderMoreSpecificAndPreserveSignatureFields() { Assert.assertEquals(unifiedHeader.getSignature(), "signed"); Assert.assertEquals(unifiedHeader.getSerial(), "serial-no"); } + + @Test + public void shouldIncludeTimestampAndNonceInLegacyHeaderEquality() { + com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader first = + new com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader("timestamp-1", "nonce", "signed", "serial-no"); + com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader second = + new com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader("timestamp-2", "nonce", "signed", "serial-no"); + + Assert.assertNotEquals(first, second); + } + + @Test + public void shouldReadLegacySerializedHeaderFields() throws Exception { + String legacySerializedHeader = "rO0ABXNyADpjb20uZ2l0aHViLmJpbmFyeXdhbmcud3hwYXkuYmVhbi5lY29tbWVyY2UuU2lnbmF0dXJlSGVhZGVyn3ApxLekv9MCAARMAAVub25jZXQAEkxqYXZhL2xhbmcvU3RyaW5nO0wACHNlcmlhbE5vcQB+AAFMAAZzaWduZWRxAH4AAUwACXRpbWVTdGFtcHEAfgABeHB0AAVub25jZXQACXNlcmlhbC1ub3QABnNpZ25lZHQACXRpbWVzdGFtcA=="; + ObjectInputStream input = new ObjectInputStream(new ByteArrayInputStream( + Base64.getDecoder().decode(legacySerializedHeader))); + com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader header = + (com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader) input.readObject(); + + Assert.assertEquals(header.getTimeStamp(), "timestamp"); + Assert.assertEquals(header.getNonce(), "nonce"); + Assert.assertEquals(header.getSigned(), "signed"); + Assert.assertEquals(header.getSerialNo(), "serial-no"); + } + + private void shouldCompileNullNotificationHeaderCalls(EcommerceService ecommerceService) throws Exception { + ecommerceService.parseRefundNotifyResult("notify-data", null); + ecommerceService.parseWithdrawNotifyResult("notify-data", null); + } }