Browse Source

修复:商品描述过长导致支付失败的bug

main
科技小王子 2 weeks ago
parent
commit
a5c9dda2a1
  1. 111
      src/main/java/com/gxwebsoft/common/core/utils/WechatPayUtils.java
  2. 43
      src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java
  3. 46
      src/test/java/com/gxwebsoft/shop/WechatPayDescriptionTest.java

111
src/main/java/com/gxwebsoft/common/core/utils/WechatPayUtils.java

@ -0,0 +1,111 @@
package com.gxwebsoft.common.core.utils;
import java.nio.charset.StandardCharsets;
/**
* 微信支付工具类
* 处理微信支付API的字段限制和格式要求
*
* @author 科技小王子
* @since 2025-01-11
*/
public class WechatPayUtils {
/**
* 微信支付description字段的最大字节数限制
*/
public static final int DESCRIPTION_MAX_BYTES = 127;
/**
* 微信支付attach字段的最大字节数限制
*/
public static final int ATTACH_MAX_BYTES = 127;
/**
* 截断字符串以确保字节数不超过指定限制
* 主要用于微信支付API的字段限制处理
*
* @param text 原始文本
* @param maxBytes 最大字节数
* @return 截断后的文本确保UTF-8字符完整性
*/
public static 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 ""; // 如果无法安全截断,返回空字符串
}
/**
* 处理微信支付商品描述字段
* 确保字节数不超过127字节
*
* @param description 商品描述
* @return 处理后的描述符合微信支付要求
*/
public static String processDescription(String description) {
return truncateToByteLimit(description, DESCRIPTION_MAX_BYTES);
}
/**
* 处理微信支付附加数据字段
* 确保字节数不超过127字节
*
* @param attach 附加数据
* @return 处理后的附加数据符合微信支付要求
*/
public static String processAttach(String attach) {
return truncateToByteLimit(attach, ATTACH_MAX_BYTES);
}
/**
* 验证字符串是否符合微信支付字段的字节限制
*
* @param text 待验证的文本
* @param maxBytes 最大字节数限制
* @return true如果符合限制false如果超出限制
*/
public static boolean isWithinByteLimit(String text, int maxBytes) {
if (text == null) {
return true;
}
return text.getBytes(StandardCharsets.UTF_8).length <= maxBytes;
}
/**
* 获取字符串的UTF-8字节数
*
* @param text 文本
* @return 字节数
*/
public static int getByteLength(String text) {
if (text == null) {
return 0;
}
return text.getBytes(StandardCharsets.UTF_8).length;
}
}

43
src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java

@ -142,11 +142,7 @@ import com.gxwebsoft.common.core.service.PaymentCacheService;
request.setAppid(payment.getAppId()); request.setAppid(payment.getAppId());
request.setMchid(payment.getMchId()); request.setMchid(payment.getMchId());
// 微信支付description字段限制127字节,需要截断处理 // 微信支付description字段限制127字节,需要截断处理
String description = order.getComments();
if (description != null) {
// 确保字节数不超过127字节
description = truncateToByteLimit(description, 127);
}
String description = com.gxwebsoft.common.core.utils.WechatPayUtils.processDescription(order.getComments());
request.setDescription(description); request.setDescription(description);
request.setOutTradeNo(order.getOrderNo()); request.setOutTradeNo(order.getOrderNo());
request.setAttach(order.getTenantId().toString()); request.setAttach(order.getTenantId().toString());
@ -673,43 +669,6 @@ 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 ""; // 如果无法安全截断,返回空字符串
}
} }

46
src/test/java/com/gxwebsoft/shop/WechatPayDescriptionTest.java

@ -1,5 +1,6 @@
package com.gxwebsoft.shop; package com.gxwebsoft.shop;
import com.gxwebsoft.common.core.utils.WechatPayUtils;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
@ -14,45 +15,6 @@ import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest @SpringBootTest
public class WechatPayDescriptionTest { 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 @Test
public void testTruncateToByteLimit() { public void testTruncateToByteLimit() {
// 测试正常情况 - 字符串长度在限制内 // 测试正常情况 - 字符串长度在限制内
@ -91,7 +53,7 @@ public class WechatPayDescriptionTest {
public void testSpecificErrorCase() { public void testSpecificErrorCase() {
// 测试错误信息中的具体案例 // 测试错误信息中的具体案例
String errorText = "【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋"; String errorText = "【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋";
// 验证原始文本确实超过127字节 // 验证原始文本确实超过127字节
int originalBytes = errorText.getBytes(StandardCharsets.UTF_8).length; int originalBytes = errorText.getBytes(StandardCharsets.UTF_8).length;
System.out.println("原始文本: " + errorText); System.out.println("原始文本: " + errorText);
@ -101,10 +63,10 @@ public class WechatPayDescriptionTest {
// 测试截断 // 测试截断
String truncated = truncateToByteLimit(errorText, 127); String truncated = truncateToByteLimit(errorText, 127);
int truncatedBytes = truncated.getBytes(StandardCharsets.UTF_8).length; int truncatedBytes = truncated.getBytes(StandardCharsets.UTF_8).length;
System.out.println("截断后文本: " + truncated); System.out.println("截断后文本: " + truncated);
System.out.println("截断后字节数: " + truncatedBytes); System.out.println("截断后字节数: " + truncatedBytes);
// 验证截断后的文本符合要求 // 验证截断后的文本符合要求
assertNotNull(truncated); assertNotNull(truncated);
assertTrue(truncatedBytes <= 127, "截断后字节数应该不超过127"); assertTrue(truncatedBytes <= 127, "截断后字节数应该不超过127");

Loading…
Cancel
Save