PAYATHON 2026

扫码支付后,支付宝账单标题乱码且无法显示收付方名称

支付小周

结论

账单标题乱码通常不是支付宝客户端造成的,而是商户创建扫码支付订单时,对 subject、body 等文本字段使用了错误的字符编码,或进行了重复编码。构造请求时应统一使用 UTF-8,并确保参与签名的内容与实际发送的内容完全一致。

如果账单标题显示正常,却没有显示真实的收款方或付款方名称,问题还可能与商户账户资料、接口传参规则或支付宝的隐私展示策略有关。修改账单标题并不能强制显示交易对手的真实名称。

常见原因

1. 请求字符集不一致

接口虽然声明了 charset=utf-8,程序却使用 GBK、GB2312 或系统默认编码处理 subject,支付宝收到的中文便可能变成乱码。

这类问题常见于以下情况:

  • Java 调用 String.getBytes(),依赖操作系统的默认字符集;
  • PHP 文件没有使用 UTF-8 编码;
  • 从数据库读取数据时发生了转码;
  • 网关、反向代理或 HTTP 客户端修改了请求编码;
  • 接口声明的 charset 与签名前实际使用的编码不同。

2. 中文被重复 URL 编码

例如,原始标题是:

测试商品

正常进行一次 URL 编码后可能是:

%E6%B5%8B%E8%AF%95%E5%95%86%E5%93%81

如果业务代码先手动执行 URLEncoder.encode(),SDK 或 HTTP 客户端随后又编码一次,支付宝可能会把编码字符串本身当作标题。因此,账单中会出现 %E6...、%25E6... 或其他异常内容。

3. 签名与发送时使用了不同的文本

有些程序使用原始中文生成签名,之后又对参数转码。也有程序先编码再签名,但 SDK 发送请求时再次编码。即使接口没有直接返回签名错误,字段内容也可能出现异常。

需要遵守的原则是:

参与签名的业务内容 = 实际提交的业务内容

具体的签名过程应交给对应语言的支付宝 SDK。不要先自行拼接或转换编码,再把处理后的内容交给 SDK。

4. 订单标题字段填写不正确

扫码支付接口通常使用 subject 表示订单标题。如果该字段传入的是商户内部编码、转义后的 JSON、Base64 文本或带有不可见字符的内容,账单自然无法显示预期名称。

subject 只是订单标题,并不代表收款方或付款方的实名信息。

5. 名称受账户资料或隐私规则影响

如果标题没有乱码,只是没有显示完整的收付款方名称,可能存在以下原因:

  • 收款账户的商户名称或签约资料配置不正确;
  • 当前交易产品只显示商户简称或订单标题;
  • 个人账户名称经过了脱敏处理;
  • 支付宝客户端不同页面采用不同的展示规则;
  • 支付宝会根据交易类型、账户类型和风控策略生成账单展示信息。

账单最终显示哪些字段由支付宝决定。传入 subject 并不意味着账单一定会显示真实的交易对手名称。

解决步骤

第一步:检查创建订单时的原始参数

调用接口前,记录以下内容:

  • subject 的原始值;
  • body 的原始值;
  • charset;
  • 请求的 Content-Type;
  • 使用的 SDK 和接口名称;
  • 数据进入支付模块前是否已经进行了 URL 编码。

日志中不要记录付款码、密钥、完整用户标识等敏感信息。

可以先用一个简单标题测试:

扫码支付测试订单

如果简单中文仍然乱码,问题很可能出在字符集或请求构造过程中。

第二步:统一使用 UTF-8

Java 代码不要依赖默认字符集:

import java.nio.charset.StandardCharsets;

String subject = "扫码支付测试订单";
byte[] bytes = subject.getBytes(StandardCharsets.UTF_8);
String normalizedSubject = new String(bytes, StandardCharsets.UTF_8);

使用官方 SDK 时,一般可以直接传入 Java String,不需要先转换为字节,也不需要执行 URL 编码:

AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();

AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
model.setOutTradeNo("ORDER_202609090001");
model.setTotalAmount("0.01");
model.setSubject("扫码支付测试订单");

request.setBizModel(model);

接口类和模型类应以项目实际使用的支付宝 SDK 为准。

PHP 源文件、配置文件和数据库连接也应统一使用 UTF-8:

$bizContent = [
    'out_trade_no' => 'ORDER_202609090001',
    'total_amount' => '0.01',
    'subject' => '扫码支付测试订单',
];

$request->setBizContent(
    json_encode($bizContent, JSON_UNESCAPED_UNICODE)
);

JSON_UNESCAPED_UNICODE 可以让日志和调试信息更易读,但 \uXXXX 本身是合法的 JSON 表示形式,不一定会导致乱码。真正需要检查的是,数据在读取、序列化和发送过程中是否始终使用 UTF-8。

第三步:删除不必要的手动编码

使用 SDK 时,通常不要这样处理:

String subject = URLEncoder.encode(
    "扫码支付测试订单",
    StandardCharsets.UTF_8
);
model.setSubject(subject);

直接传入原始文本即可:

model.setSubject("扫码支付测试订单");

如果没有使用 SDK,需要自行构造表单请求,只在 HTTP 表单序列化阶段编码一次:

String formValue = URLEncoder.encode(
    "扫码支付测试订单",
    StandardCharsets.UTF_8
);

不要再次对已经编码的 formValue 执行 URLEncoder.encode()。

第四步:检查数据库与中间服务

如果订单标题来自数据库,可以先检查:

SHOW VARIABLES LIKE 'character_set%';
SHOW VARIABLES LIKE 'collation%';

数据库、表、字段和连接所用的字符集应保持一致。MySQL 项目通常建议使用 utf8mb4。JDBC 连接也需要确认启用了 Unicode:

jdbc:mysql://127.0.0.1:3306/payment?useUnicode=true&characterEncoding=UTF-8

消息队列、缓存、网关和其他中间服务也需要逐一检查,确认它们没有把 UTF-8 字节按其他字符集重新解释。

第五步:创建新订单验证

修复编码问题后,需要创建一笔新订单进行验证。历史账单通常保留交易创建时提交的信息。即使程序或商户资料已经修改,已生成的账单标题也不一定会自动更新。

建议分别测试以下标题来源和内容:

  • 纯中文标题;
  • 中英文混合标题;
  • 带有括号、连字符等常用符号的标题;
  • 不经过数据库、直接写入代码的标题;
  • 从数据库读取的标题。

这些测试可以帮助判断乱码发生在业务数据源、支付程序还是请求传输阶段。

如何判断问题属于哪一类

账单显示 %E6%B5%8B...,通常说明 URL 编码后的内容被当作普通文本提交了。

显示类似 æµè¯ 的内容,通常说明 UTF-8 字节被当作 ISO-8859-1 或其他字符集解释。

如果显示 ???,中文可能在进入支付接口前就被不支持中文的字符集替换了,原始信息往往已经丢失。

如果订单标题中的中文正常,只是收付款方名称被隐藏、缩写或脱敏,应重点检查商户签约资料和支付宝的展示规则,不必继续修改字符编码。

注意事项

  • subject 应填写真实、简洁的订单标题,不要包含付款码、用户隐私或内部敏感信息。
  • 不要使用 subject 冒充收款方名称,账单中的交易对手信息并不完全由该字段控制。
  • 优先使用支付宝官方 SDK 处理参数序列化和签名。
  • 字符编码必须在签名前调整完成。
  • 排查时可以记录标题及其 UTF-8 十六进制值,但不要输出应用私钥、支付宝公钥、付款码或完整请求签名。
  • 如果请求参数确认无误,新交易仍然持续出现乱码,应整理接口名称、请求时间、支付宝交易号、脱敏后的请求参数和问题截图,再通过支付宝开放平台的技术支持渠道核查具体的账单展示规则。

备注:内容仅供参考。