修复:商品描述过长导致支付失败的bug
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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();
|
String description = com.gxwebsoft.common.core.utils.WechatPayUtils.processDescription(order.getComments());
|
||||||
if (description != null) {
|
|
||||||
// 确保字节数不超过127字节
|
|
||||||
description = truncateToByteLimit(description, 127);
|
|
||||||
}
|
|
||||||
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 ""; // 如果无法安全截断,返回空字符串
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() {
|
||||||
// 测试正常情况 - 字符串长度在限制内
|
// 测试正常情况 - 字符串长度在限制内
|
||||||
|
|||||||
Reference in New Issue
Block a user