修复:商品描述过长导致支付失败的bug
This commit is contained in:
@@ -141,7 +141,13 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
request.setAmount(amount);
|
||||
request.setAppid(payment.getAppId());
|
||||
request.setMchid(payment.getMchId());
|
||||
request.setDescription(order.getComments());
|
||||
// 微信支付description字段限制127字节,需要截断处理
|
||||
String description = order.getComments();
|
||||
if (description != null) {
|
||||
// 确保字节数不超过127字节
|
||||
description = truncateToByteLimit(description, 127);
|
||||
}
|
||||
request.setDescription(description);
|
||||
request.setOutTradeNo(order.getOrderNo());
|
||||
request.setAttach(order.getTenantId().toString());
|
||||
final Payer payer = new Payer();
|
||||
@@ -667,4 +673,43 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 截断字符串以确保字节数不超过指定限制
|
||||
* 微信支付description字段限制127字节
|
||||
*
|
||||
* @param text 原始文本
|
||||
* @param maxBytes 最大字节数
|
||||
* @return 截断后的文本
|
||||
*/
|
||||
private String truncateToByteLimit(String text, int maxBytes) {
|
||||
if (text == null || text.isEmpty()) {
|
||||
return text;
|
||||
}
|
||||
|
||||
byte[] bytes = text.getBytes(java.nio.charset.StandardCharsets.UTF_8);
|
||||
if (bytes.length <= maxBytes) {
|
||||
return text;
|
||||
}
|
||||
|
||||
// 截断字节数组,但要确保不会截断UTF-8字符的中间
|
||||
int truncateLength = maxBytes;
|
||||
while (truncateLength > 0) {
|
||||
byte[] truncated = new byte[truncateLength];
|
||||
System.arraycopy(bytes, 0, truncated, 0, truncateLength);
|
||||
|
||||
try {
|
||||
String result = new String(truncated, java.nio.charset.StandardCharsets.UTF_8);
|
||||
// 检查是否有无效字符(被截断的UTF-8字符)
|
||||
if (!result.contains("\uFFFD")) {
|
||||
return result;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 继续尝试更短的长度
|
||||
}
|
||||
truncateLength--;
|
||||
}
|
||||
|
||||
return ""; // 如果无法安全截断,返回空字符串
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
113
src/test/java/com/gxwebsoft/shop/WechatPayDescriptionTest.java
Normal file
113
src/test/java/com/gxwebsoft/shop/WechatPayDescriptionTest.java
Normal file
@@ -0,0 +1,113 @@
|
||||
package com.gxwebsoft.shop;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* 微信支付商品描述字段测试
|
||||
* 验证字节长度限制处理
|
||||
*/
|
||||
@SpringBootTest
|
||||
public class WechatPayDescriptionTest {
|
||||
|
||||
/**
|
||||
* 截断字符串以确保字节数不超过指定限制
|
||||
* 微信支付description字段限制127字节
|
||||
*
|
||||
* @param text 原始文本
|
||||
* @param maxBytes 最大字节数
|
||||
* @return 截断后的文本
|
||||
*/
|
||||
private String truncateToByteLimit(String text, int maxBytes) {
|
||||
if (text == null || text.isEmpty()) {
|
||||
return text;
|
||||
}
|
||||
|
||||
byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
|
||||
if (bytes.length <= maxBytes) {
|
||||
return text;
|
||||
}
|
||||
|
||||
// 截断字节数组,但要确保不会截断UTF-8字符的中间
|
||||
int truncateLength = maxBytes;
|
||||
while (truncateLength > 0) {
|
||||
byte[] truncated = new byte[truncateLength];
|
||||
System.arraycopy(bytes, 0, truncated, 0, truncateLength);
|
||||
|
||||
try {
|
||||
String result = new String(truncated, StandardCharsets.UTF_8);
|
||||
// 检查是否有无效字符(被截断的UTF-8字符)
|
||||
if (!result.contains("\uFFFD")) {
|
||||
return result;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 继续尝试更短的长度
|
||||
}
|
||||
truncateLength--;
|
||||
}
|
||||
|
||||
return ""; // 如果无法安全截断,返回空字符串
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTruncateToByteLimit() {
|
||||
// 测试正常情况 - 字符串长度在限制内
|
||||
String shortText = "正常商品描述";
|
||||
String result1 = truncateToByteLimit(shortText, 127);
|
||||
assertEquals(shortText, result1);
|
||||
assertTrue(result1.getBytes(StandardCharsets.UTF_8).length <= 127);
|
||||
|
||||
// 测试超长中文字符串
|
||||
String longText = "【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋";
|
||||
String result2 = truncateToByteLimit(longText, 127);
|
||||
assertNotNull(result2);
|
||||
assertTrue(result2.getBytes(StandardCharsets.UTF_8).length <= 127);
|
||||
System.out.println("原始文本字节数: " + longText.getBytes(StandardCharsets.UTF_8).length);
|
||||
System.out.println("截断后文本: " + result2);
|
||||
System.out.println("截断后字节数: " + result2.getBytes(StandardCharsets.UTF_8).length);
|
||||
|
||||
// 测试null和空字符串
|
||||
assertNull(truncateToByteLimit(null, 127));
|
||||
assertEquals("", truncateToByteLimit("", 127));
|
||||
|
||||
// 测试英文字符串
|
||||
String englishText = "This is a very long English description that might exceed the byte limit for WeChat Pay description field";
|
||||
String result3 = truncateToByteLimit(englishText, 127);
|
||||
assertNotNull(result3);
|
||||
assertTrue(result3.getBytes(StandardCharsets.UTF_8).length <= 127);
|
||||
|
||||
// 测试混合字符串
|
||||
String mixedText = "Product Name 产品名称 with special characters @#$%^&*()";
|
||||
String result4 = truncateToByteLimit(mixedText, 50);
|
||||
assertNotNull(result4);
|
||||
assertTrue(result4.getBytes(StandardCharsets.UTF_8).length <= 50);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpecificErrorCase() {
|
||||
// 测试错误信息中的具体案例
|
||||
String errorText = "【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋";
|
||||
|
||||
// 验证原始文本确实超过127字节
|
||||
int originalBytes = errorText.getBytes(StandardCharsets.UTF_8).length;
|
||||
System.out.println("原始文本: " + errorText);
|
||||
System.out.println("原始字节数: " + originalBytes);
|
||||
assertTrue(originalBytes > 127, "原始文本应该超过127字节");
|
||||
|
||||
// 测试截断
|
||||
String truncated = truncateToByteLimit(errorText, 127);
|
||||
int truncatedBytes = truncated.getBytes(StandardCharsets.UTF_8).length;
|
||||
|
||||
System.out.println("截断后文本: " + truncated);
|
||||
System.out.println("截断后字节数: " + truncatedBytes);
|
||||
|
||||
// 验证截断后的文本符合要求
|
||||
assertNotNull(truncated);
|
||||
assertTrue(truncatedBytes <= 127, "截断后字节数应该不超过127");
|
||||
assertFalse(truncated.contains("\uFFFD"), "截断后的文本不应包含无效字符");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user