1
0
mirror of synced 2025-12-16 20:28:11 +08:00

Compare commits

..

8 Commits

44 changed files with 226 additions and 197 deletions

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
<packaging>pom</packaging>
<name>WxJava - Weixin/Wechat Java SDK</name>
<description>微信开发Java SDK</description>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<packaging>pom</packaging>
<artifactId>wx-java-solon-plugins</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>wx-java-solon-plugins</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<packaging>pom</packaging>
<artifactId>wx-java-spring-boot-starters</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -4,7 +4,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>wx-java-spring-boot-starters</artifactId>
<groupId>com.github.binarywang</groupId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<artifactId>weixin-graal</artifactId>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<artifactId>weixin-java-channel</artifactId>

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<artifactId>weixin-java-common</artifactId>

View File

@@ -1,18 +1,11 @@
package me.chanjar.weixin.common.util.locks;
import lombok.Getter;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
@@ -72,17 +65,16 @@ public class RedisTemplateSimpleDistributedLock implements Lock {
value = UUID.randomUUID().toString();
valueThreadLocal.set(value);
}
RedisSerializer<String> keySerializer = (RedisSerializer<String>) redisTemplate.getKeySerializer();
RedisSerializer<String> valueSerializer = (RedisSerializer<String>) redisTemplate.getValueSerializer();
final byte[] keyBytes = Objects.requireNonNull(keySerializer.serialize(key));
final byte[] valueBytes = Objects.requireNonNull(valueSerializer.serialize(value));
List<Object> redisResults = redisTemplate.executePipelined((RedisCallback<String>) connection -> {
connection.set(keyBytes, valueBytes, Expiration.milliseconds(leaseMilliseconds), RedisStringCommands.SetOption.SET_IF_ABSENT);
connection.get(keyBytes);
return null;
});
Object currentLockSecret = redisResults.size() > 1 ? redisResults.get(1) : redisResults.get(0);
return currentLockSecret != null && currentLockSecret.toString().equals(value);
// Use high-level StringRedisTemplate API to ensure consistent key serialization
Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(key, value, leaseMilliseconds, TimeUnit.MILLISECONDS);
if (Boolean.TRUE.equals(lockAcquired)) {
return true;
}
// Check if we already hold the lock (reentrant behavior)
String currentValue = redisTemplate.opsForValue().get(key);
return value.equals(currentValue);
}
@Override

View File

@@ -0,0 +1,100 @@
package me.chanjar.weixin.common.util.locks;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
/**
* 测试 RedisTemplateSimpleDistributedLock 在自定义 Key 序列化时的兼容性
*
* 这个测试验证修复后的实现确保 tryLock 和 unlock 使用一致的键序列化方式
*/
@Test(enabled = false) // 默认禁用需要Redis实例才能运行
public class RedisTemplateSimpleDistributedLockSerializationTest {
private RedisTemplateSimpleDistributedLock redisLock;
private StringRedisTemplate redisTemplate;
@BeforeTest
public void init() {
JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
connectionFactory.setHostName("127.0.0.1");
connectionFactory.setPort(6379);
connectionFactory.afterPropertiesSet();
// 创建一个带自定义键序列化的 StringRedisTemplate
StringRedisTemplate redisTemplate = new StringRedisTemplate(connectionFactory);
// 使用自定义键序列化器,模拟在键前面添加前缀的场景
redisTemplate.setKeySerializer(new StringRedisSerializer() {
@Override
public byte[] serialize(String string) {
if (string == null) return null;
// 添加 "System:" 前缀,模拟用户自定义的键序列化
return super.serialize("System:" + string);
}
@Override
public String deserialize(byte[] bytes) {
if (bytes == null) return null;
String result = super.deserialize(bytes);
// 移除前缀进行反序列化
return result != null && result.startsWith("System:") ? result.substring(7) : result;
}
});
this.redisTemplate = redisTemplate;
this.redisLock = new RedisTemplateSimpleDistributedLock(redisTemplate, "test_lock_key", 60000);
}
@Test(description = "测试自定义键序列化器下的锁操作一致性")
public void testLockConsistencyWithCustomKeySerializer() {
// 1. 获取锁应该成功
assertTrue(redisLock.tryLock(), "第一次获取锁应该成功");
assertNotNull(redisLock.getLockSecretValue(), "锁值应该存在");
// 2. 验证键已正确存储(通过 redisTemplate 直接查询)
String actualValue = redisTemplate.opsForValue().get("test_lock_key");
assertEquals(actualValue, redisLock.getLockSecretValue(), "通过 redisTemplate 查询的值应该与锁值相同");
// 3. 再次尝试获取同一把锁应该成功(可重入)
assertTrue(redisLock.tryLock(), "可重入锁应该再次获取成功");
// 4. 释放锁应该成功
redisLock.unlock();
assertNull(redisLock.getLockSecretValue(), "释放锁后锁值应该为空");
// 5. 验证键已被删除
actualValue = redisTemplate.opsForValue().get("test_lock_key");
assertNull(actualValue, "释放锁后 Redis 中的键应该被删除");
// 6. 释放已释放的锁应该是安全的
redisLock.unlock(); // 不应该抛出异常
}
@Test(description = "测试不同线程使用相同键的锁排他性")
public void testLockExclusivityWithCustomKeySerializer() throws InterruptedException {
// 第一个锁实例获取锁
assertTrue(redisLock.tryLock(), "第一个锁实例应该成功获取锁");
// 创建第二个锁实例使用相同的键
RedisTemplateSimpleDistributedLock anotherLock = new RedisTemplateSimpleDistributedLock(
redisTemplate, "test_lock_key", 60000);
// 第二个锁实例不应该能获取锁
assertFalse(anotherLock.tryLock(), "第二个锁实例不应该能获取已被占用的锁");
// 释放第一个锁
redisLock.unlock();
// 现在第二个锁实例应该能获取锁
assertTrue(anotherLock.tryLock(), "第一个锁释放后,第二个锁实例应该能获取锁");
// 清理
anotherLock.unlock();
}
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<artifactId>weixin-java-cp</artifactId>

View File

@@ -168,9 +168,13 @@ public class WxCpAgentWorkBench implements Serializable {
webview.addProperty("url", this.url);
webview.addProperty("jump_url", this.jumpUrl);
webview.addProperty("pagepath", this.pagePath);
webview.addProperty("enable_webview_click", this.enableWebviewClick);
if (this.enableWebviewClick != null) {
webview.addProperty("enable_webview_click", this.enableWebviewClick);
}
webview.addProperty("height", this.height);
webview.addProperty("hide_title", this.hideTitle);
if (this.hideTitle != null) {
webview.addProperty("hide_title", this.hideTitle);
}
templateObject.add("webview", webview);
break;
}
@@ -236,9 +240,13 @@ public class WxCpAgentWorkBench implements Serializable {
webview.addProperty("url", this.url);
webview.addProperty("jump_url", this.jumpUrl);
webview.addProperty("pagepath", this.pagePath);
webview.addProperty("enable_webview_click", this.enableWebviewClick);
if (this.enableWebviewClick != null) {
webview.addProperty("enable_webview_click", this.enableWebviewClick);
}
webview.addProperty("height", this.height);
webview.addProperty("hide_title", this.hideTitle);
if (this.hideTitle != null) {
webview.addProperty("hide_title", this.hideTitle);
}
JsonObject dataObject = new JsonObject();
dataObject.addProperty("type", WxCpConsts.WorkBenchType.WEBVIEW);
dataObject.add("webview", webview);

View File

@@ -92,7 +92,7 @@ public class WxCpXmlMessage implements Serializable {
private String content;
@XStreamAlias("MsgId")
private Long msgId;
private String msgId;
@XStreamAlias("PicUrl")
@XStreamConverter(value = XStreamCDataConverter.class)
@@ -159,6 +159,14 @@ public class WxCpXmlMessage implements Serializable {
@XStreamConverter(value = XStreamCDataConverter.class)
private String MemChangeList;
@XStreamAlias("LastMemVer")
@XStreamConverter(value = XStreamCDataConverter.class)
private String lastMemVer;
@XStreamAlias("CurMemVer")
@XStreamConverter(value = XStreamCDataConverter.class)
private String curMemVer;
@XStreamAlias("Source")
@XStreamConverter(value = XStreamCDataConverter.class)
private String source;

View File

@@ -72,7 +72,7 @@ public class WxCpXmlMessageTest {
assertEquals(wxMessage.getCreateTime(), Long.valueOf(1348831860));
assertEquals(wxMessage.getMsgType(), WxConsts.XmlMsgType.TEXT);
assertEquals(wxMessage.getContent(), "this is a test");
assertEquals(wxMessage.getMsgId(), Long.valueOf(1234567890123456L));
assertEquals(wxMessage.getMsgId(), "1234567890123456");
assertEquals(wxMessage.getPicUrl(), "this is a url");
assertEquals(wxMessage.getMediaId(), "media_id");
assertEquals(wxMessage.getFormat(), "Format");
@@ -442,4 +442,33 @@ public class WxCpXmlMessageTest {
assertThat(wxCpXmlMessage.getJobId()).isEqualTo("jobid_S0MrnndvRG5fadSlLwiBqiDDbM143UqTmKP3152FZk4");
assertThat(wxCpXmlMessage.getEvent()).isEqualTo(UPLOAD_MEDIA_JOB_FINISH);
}
/**
* Test both numeric and string msgId formats to ensure backward compatibility
*/
public void testMsgIdStringAndNumericFormats() {
// Test with numeric msgId (old format)
String xmlWithNumeric = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>"
+ "<CreateTime>1348831860</CreateTime>"
+ "<MsgType><![CDATA[text]]></MsgType>"
+ "<Content><![CDATA[this is a test]]></Content>"
+ "<MsgId>1234567890123456</MsgId>"
+ "</xml>";
WxCpXmlMessage wxMessageNumeric = WxCpXmlMessage.fromXml(xmlWithNumeric);
assertEquals(wxMessageNumeric.getMsgId(), "1234567890123456");
// Test with string msgId (new format - the actual issue case)
String xmlWithString = "<xml>"
+ "<ToUserName><![CDATA[toUser]]></ToUserName>"
+ "<FromUserName><![CDATA[fromUser]]></FromUserName>"
+ "<CreateTime>1348831860</CreateTime>"
+ "<MsgType><![CDATA[text]]></MsgType>"
+ "<Content><![CDATA[this is a test]]></Content>"
+ "<MsgId>CAIQg/PKxgYY2sC9tpuAgAMg9/zKaw==</MsgId>"
+ "</xml>";
WxCpXmlMessage wxMessageString = WxCpXmlMessage.fromXml(xmlWithString);
assertEquals(wxMessageString.getMsgId(), "CAIQg/PKxgYY2sC9tpuAgAMg9/zKaw==");
}
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<artifactId>weixin-java-miniapp</artifactId>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<artifactId>weixin-java-mp</artifactId>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<artifactId>weixin-java-open</artifactId>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -29,7 +29,6 @@ import org.apache.http.ssl.SSLContexts;
import javax.net.ssl.SSLContext;
import java.io.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
@@ -432,14 +431,7 @@ public class WxPayConfig {
}
if (StringUtils.isNotEmpty(configString)) {
// 判断是否为PEM格式的字符串包含-----BEGIN和-----END标记
if (configString.contains("-----BEGIN") && configString.contains("-----END")) {
// PEM格式直接转为字节流让PemUtils处理
configContent = configString.getBytes(StandardCharsets.UTF_8);
} else {
// 纯Base64格式需要先解码
configContent = Base64.getDecoder().decode(configString);
}
configContent = Base64.getDecoder().decode(configString);
return new ByteArrayInputStream(configContent);
}

View File

@@ -8,11 +8,13 @@ import com.github.binarywang.wxpay.service.WxEntrustPapService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.util.SignUtils;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.util.json.WxGsonBuilder;
import org.apache.commons.lang3.StringUtils;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
/**
* @author chenliang
@@ -26,26 +28,40 @@ public class WxEntrustPapServiceImpl implements WxEntrustPapService {
@Override
@SneakyThrows
public String mpSign(WxMpEntrustRequest wxMpEntrustRequest) throws WxPayException {
wxMpEntrustRequest.checkAndSign(payService.getConfig());
StringBuilder signStrTemp = new StringBuilder(payService.getPayBaseUrl() + "/papay/entrustweb");
signStrTemp.append("?appid=").append(wxMpEntrustRequest.getAppid());
signStrTemp.append("&contract_code=").append(wxMpEntrustRequest.getContractCode());
signStrTemp.append("&contract_display_account=").append(URLEncoder.encode(wxMpEntrustRequest.getContractDisplayAccount()));
signStrTemp.append("&mch_id=").append(wxMpEntrustRequest.getMchId()).append("&notify_url=").append(URLEncoder.encode(wxMpEntrustRequest.getNotifyUrl()));
signStrTemp.append("&plan_id=").append(wxMpEntrustRequest.getPlanId()).append("&outerid=").append(URLEncoder.encode(wxMpEntrustRequest.getOuterId()));
signStrTemp.append("&request_serial=").append(wxMpEntrustRequest.getRequestSerial()).append("&timestamp=").append(wxMpEntrustRequest.getTimestamp());
signStrTemp.append("&version=").append(wxMpEntrustRequest.getVersion()).append("&return_web=").append(wxMpEntrustRequest.getReturnWeb()).append("&sign=").append(wxMpEntrustRequest.getSign());
signStrTemp.append("&contract_display_account=")
.append(URLEncoder.encode(wxMpEntrustRequest.getContractDisplayAccount(), StandardCharsets.UTF_8.name()));
signStrTemp.append("&mch_id=").append(wxMpEntrustRequest.getMchId()).append("&notify_url=")
.append(URLEncoder.encode(wxMpEntrustRequest.getNotifyUrl(), StandardCharsets.UTF_8.name()));
signStrTemp.append("&plan_id=").append(wxMpEntrustRequest.getPlanId());
signStrTemp.append("&request_serial=").append(wxMpEntrustRequest.getRequestSerial()).append("&timestamp=")
.append(wxMpEntrustRequest.getTimestamp());
// 根据微信支付文档returnWeb字段只在值为1时需要添加到URL参数中表示返回签约页面的referrer url
if (wxMpEntrustRequest.getReturnWeb() != null && wxMpEntrustRequest.getReturnWeb() == 1) {
signStrTemp.append("&return_web=").append(wxMpEntrustRequest.getReturnWeb());
}
if (StringUtils.isNotEmpty(wxMpEntrustRequest.getOuterId())) {
signStrTemp.append("&outerid=").append(URLEncoder.encode(wxMpEntrustRequest.getOuterId(), StandardCharsets.UTF_8.name()));
}
signStrTemp.append("&version=").append(wxMpEntrustRequest.getVersion()).append("&sign=")
.append(wxMpEntrustRequest.getSign());
return signStrTemp.toString();
}
@Override
@SneakyThrows
public String maSign(WxMaEntrustRequest wxMaEntrustRequest) throws WxPayException {
wxMaEntrustRequest.checkAndSign(payService.getConfig());
wxMaEntrustRequest.setNotifyUrl(URLEncoder.encode(wxMaEntrustRequest.getNotifyUrl()));
wxMaEntrustRequest.setNotifyUrl(URLEncoder.encode(wxMaEntrustRequest.getNotifyUrl(), StandardCharsets.UTF_8.name()));
return wxMaEntrustRequest.toString();
}
@SneakyThrows
@Override
public WxH5EntrustResult h5Sign(WxH5EntrustRequest wxH5EntrustRequest) throws WxPayException {
wxH5EntrustRequest.checkAndSign(payService.getConfig());
@@ -64,11 +80,11 @@ public class WxEntrustPapServiceImpl implements WxEntrustPapService {
StringBuilder strBuilder = new StringBuilder(url);
strBuilder.append("?appid=").append(wxH5EntrustRequest.getAppid());
strBuilder.append("&contract_code=").append(wxH5EntrustRequest.getContractCode());
strBuilder.append("&contract_display_account=").append(URLEncoder.encode(wxH5EntrustRequest.getContractDisplayAccount()));
strBuilder.append("&mch_id=").append(wxH5EntrustRequest.getMchId()).append("&notify_url=").append(URLEncoder.encode(wxH5EntrustRequest.getNotifyUrl()));
strBuilder.append("&contract_display_account=").append(URLEncoder.encode(wxH5EntrustRequest.getContractDisplayAccount(), StandardCharsets.UTF_8.name()));
strBuilder.append("&mch_id=").append(wxH5EntrustRequest.getMchId()).append("&notify_url=").append(URLEncoder.encode(wxH5EntrustRequest.getNotifyUrl(), StandardCharsets.UTF_8.name()));
strBuilder.append("&plan_id=").append(wxH5EntrustRequest.getPlanId());
if (StringUtils.isNotEmpty(wxH5EntrustRequest.getOuterId())) {
strBuilder.append("&outerid=").append(URLEncoder.encode(wxH5EntrustRequest.getOuterId()));
strBuilder.append("&outerid=").append(URLEncoder.encode(wxH5EntrustRequest.getOuterId(), StandardCharsets.UTF_8.name()));
}
if (StringUtils.isNotEmpty(wxH5EntrustRequest.getReturnAppid())) {
strBuilder.append("&return_appid=").append(wxH5EntrustRequest.getReturnAppid());

View File

@@ -80,11 +80,11 @@ public class AesUtils {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(), "AES");
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce.getBytes());
SecretKeySpec key = new SecretKeySpec(apiV3Key.getBytes(StandardCharsets.UTF_8), "AES");
GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce.getBytes(StandardCharsets.UTF_8));
cipher.init(Cipher.DECRYPT_MODE, key, spec);
cipher.updateAAD(associatedData.getBytes());
cipher.updateAAD(associatedData.getBytes(StandardCharsets.UTF_8));
return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), StandardCharsets.UTF_8);
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {

View File

@@ -1,116 +0,0 @@
package com.github.binarywang.wxpay.config;
import com.github.binarywang.wxpay.exception.WxPayException;
import org.testng.annotations.Test;
import static org.testng.Assert.*;
/**
* Test cases for private key format handling in WxPayConfig
*/
public class WxPayConfigPrivateKeyTest {
@Test
public void testPrivateKeyStringFormat_PemFormat() {
WxPayConfig config = new WxPayConfig();
// Set minimal required configuration
config.setMchId("1234567890");
config.setApiV3Key("test-api-v3-key-32-characters-long");
config.setCertSerialNo("test-serial-number");
// Test with PEM format private key string that would previously fail
String pemKey = "-----BEGIN PRIVATE KEY-----\n" +
"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2pK3buBufh8Vo\n" +
"X4sfYbZ5CcPeGMnVQTGmj0b6\n" +
"-----END PRIVATE KEY-----";
config.setPrivateKeyString(pemKey);
// This should not throw a "无效的密钥格式" exception immediately
// The actual key validation will happen during HTTP client initialization
// but at least the format parsing should not fail
try {
// Try to initialize API V3 HTTP client - this might fail for other reasons
// (like invalid key content) but should not fail due to format parsing
config.initApiV3HttpClient();
// If we get here without InvalidKeySpecException, the format detection worked
} catch (WxPayException e) {
// Check that it's not the specific "无效的密钥格式" error from PemUtils
if (e.getCause() != null &&
e.getCause().getMessage() != null &&
e.getCause().getMessage().contains("无效的密钥格式")) {
fail("Private key format detection failed - PEM format was not handled correctly: " + e.getMessage());
}
// Other exceptions are acceptable for this test since we're using a dummy key
} catch (Exception e) {
// Check for the specific InvalidKeySpecException that indicates format problems
if (e.getCause() != null &&
e.getCause().getMessage() != null &&
e.getCause().getMessage().contains("无效的密钥格式")) {
fail("Private key format detection failed - PEM format was not handled correctly: " + e.getMessage());
}
// Other exceptions are acceptable for this test since we're using a dummy key
}
}
@Test
public void testPrivateKeyStringFormat_EmptyString() {
WxPayConfig config = new WxPayConfig();
// Test with empty string - should not cause format errors
config.setPrivateKeyString("");
// This should handle empty strings gracefully
// No assertion needed, just ensuring no exceptions during object creation
assertNotNull(config);
}
@Test
public void testPrivateKeyStringFormat_NullString() {
WxPayConfig config = new WxPayConfig();
// Test with null string - should not cause format errors
config.setPrivateKeyString(null);
// This should handle null strings gracefully
assertNotNull(config);
}
@Test
public void testPrivateCertStringFormat_PemFormat() {
WxPayConfig config = new WxPayConfig();
// Set minimal required configuration
config.setMchId("1234567890");
config.setApiV3Key("test-api-v3-key-32-characters-long");
// Test with PEM format certificate string that would previously fail
String pemCert = "-----BEGIN CERTIFICATE-----\n" +
"MIICdTCCAd4CAQAwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQVUxEzARBgNV\n" +
"BAsKClRlc3QgQ2VydCBEYXRhMRswGQYDVQQDDBJUZXN0IENlcnRpZmljYXRlQ0Ew\n" +
"-----END CERTIFICATE-----";
config.setPrivateCertString(pemCert);
// This should not throw a format parsing exception immediately
// The actual certificate validation will happen during HTTP client initialization
// but at least the format parsing should not fail
try {
// Try to initialize API V3 HTTP client - this might fail for other reasons
// (like invalid cert content) but should not fail due to format parsing
config.initApiV3HttpClient();
// If we get here without Base64 decoding issues, the format detection worked
} catch (Exception e) {
// Check that it's not the specific Base64 decoding error
if (e.getCause() != null &&
e.getCause().getMessage() != null &&
e.getCause().getMessage().contains("Illegal base64 character")) {
fail("Certificate format detection failed - PEM format was not handled correctly: " + e.getMessage());
}
// Other exceptions are acceptable for this test since we're using a dummy cert
}
}
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>com.github.binarywang</groupId>
<artifactId>wx-java</artifactId>
<version>4.7.7.B</version>
<version>4.7.8.B</version>
</parent>
<artifactId>weixin-java-qidian</artifactId>