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..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.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; import java.io.Serializable; @@ -13,30 +13,124 @@ * * @author cloudX */ -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor @Deprecated -public class SignatureHeader implements Serializable { +@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; + + /** + * 已签名字符串 + */ + private String signed; + + /** + * 证书序列号 + */ + private String serialNo; + /** - * 时间戳 + * 保留在旧类中的序列化字段,避免升级后反序列化旧数据时丢失。 */ private String timeStamp; /** - * 随机串 + * 保留在旧类中的序列化字段,避免升级后反序列化旧数据时丢失。 */ private String nonce; + public SignatureHeader() { + super(); + } + /** - * 已签名字符串 + * 保留 4.8.4 及以前版本的构造器签名。 */ - private String signed; + 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 的类型和方法返回值描述符。 */ - private String serialNo; + 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/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 a82dadb76..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 { @@ -25,4 +29,61 @@ 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 shouldKeepLegacySignatureHeaderConstructorAndBuilderAbi() throws Exception { + com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader legacyHeader = + com.github.binarywang.wxpay.bean.ecommerce.SignatureHeader.builder() + .timeStamp("timestamp") + .nonce("nonce") + .signed("signed") + .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 = + EcommerceService.toUnifiedSignatureHeader(legacyHeader); + Assert.assertEquals(unifiedHeader.getTimeStamp(), "timestamp"); + Assert.assertEquals(unifiedHeader.getNonce(), "nonce"); + 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); + } }