diff --git a/src/main/java/com/gxwebsoft/common/core/utils/WechatPayUtils.java b/src/main/java/com/gxwebsoft/common/core/utils/WechatPayUtils.java new file mode 100644 index 0000000..14a31e9 --- /dev/null +++ b/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; + } +} diff --git a/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java b/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java index 71b2c10..b3fd7c5 100644 --- a/src/main/java/com/gxwebsoft/shop/service/impl/ShopOrderServiceImpl.java +++ b/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.setMchid(payment.getMchId()); // 微信支付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.setOutTradeNo(order.getOrderNo()); 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 ""; // 如果无法安全截断,返回空字符串 - } } diff --git a/src/test/java/com/gxwebsoft/shop/WechatPayDescriptionTest.java b/src/test/java/com/gxwebsoft/shop/WechatPayDescriptionTest.java index f6ea883..a4f7292 100644 --- a/src/test/java/com/gxwebsoft/shop/WechatPayDescriptionTest.java +++ b/src/test/java/com/gxwebsoft/shop/WechatPayDescriptionTest.java @@ -1,5 +1,6 @@ package com.gxwebsoft.shop; +import com.gxwebsoft.common.core.utils.WechatPayUtils; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @@ -14,45 +15,6 @@ 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() { // 测试正常情况 - 字符串长度在限制内 @@ -91,7 +53,7 @@ public class WechatPayDescriptionTest { public void testSpecificErrorCase() { // 测试错误信息中的具体案例 String errorText = "【湾区认证】【百千万工程帮扶产品 通过远方320项检测 湾区认证 丰江桥佛手瓜面】独特风味低脂轻食便捷速食非油炸 720g/袋"; - + // 验证原始文本确实超过127字节 int originalBytes = errorText.getBytes(StandardCharsets.UTF_8).length; System.out.println("原始文本: " + errorText); @@ -101,10 +63,10 @@ public class WechatPayDescriptionTest { // 测试截断 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");