PAYATHON 2026

Alipay payment gateway integration 返回 ILLEGAL_SIGN 错误

支付小周

结论

ILLEGAL_SIGN 表示支付宝使用商户的 MD5 密钥复算签名后,得到的结果与请求中的签名不同。问题通常与支付金额或回调地址本身无关,而是出在以下某个环节:

  • 使用了错误的密钥,例如把 partner、登录密码或 RSA 密钥当作 MD5 密钥。
  • 测试环境的 partner、MD5 密钥和网关不属于同一套账号。
  • 签名参数有遗漏、重复,或者排序方式不正确。
  • 计算签名后又修改了金额、URL、字符集等参数。
  • 签名原文与实际发送的参数值不同,例如签名时使用了 URL 编码后的值。

仅凭示例 URL 无法验证签名是否正确,因为复算 MD5 必须知道该测试账号对应的私密 MD5 key。

正确的 MD5 签名流程

对于这类旧版 create_forex_trade 接口,通常需要按以下顺序处理。具体规则仍以该测试账号对应的接口文档为准。

  1. 准备全部业务参数。
  2. 删除 sign 和 sign_type。
  3. 删除值为 null 或空字符串的参数。
  4. 按参数名进行 ASCII 升序排列。
  5. 将参数写成 key=value,并使用 & 连接。
  6. 在字符串末尾直接追加商户 MD5 key。
  7. 按 _input_charset 指定的字符集转换为字节,然后计算 MD5。
  8. 将计算结果写入 sign,最后对各参数进行 URL 编码并发送。

签名原文应采用以下形式:

_input_charset=UTF-8&currency=USD&notify_url=http://www.alipay.com&out_trade_no=1115919735609649&partner=2088621921925641&return_url=http://www.alipay.com&sendFormat=normal&service=create_forex_trade&subject=Beautylish&total_fee=2.2YOUR_MD5_KEY

密钥通常直接拼接在字符串末尾:

...&total_fee=2.2YOUR_MD5_KEY

不能写成:

...&total_fee=2.2&key=YOUR_MD5_KEY

PHP 示例

<?php

$params = [
    'service'        => 'create_forex_trade',
    'partner'        => '2088621921925641',
    '_input_charset' => 'UTF-8',
    'subject'        => 'Beautylish',
    'out_trade_no'   => '1115919735609649',
    'currency'       => 'USD',
    'total_fee'      => '2.2',
    'notify_url'     => 'http://www.alipay.com',
    'return_url'     => 'http://www.alipay.com',
    'sendFormat'     => 'normal',
];

$md5Key = 'YOUR_TEST_ENVIRONMENT_MD5_KEY';

// 排除空值以及 sign、sign_type
$filtered = array_filter(
    $params,
    static function ($value, $name) {
        return $name !== 'sign'
            && $name !== 'sign_type'
            && $value !== null
            && $value !== '';
    },
    ARRAY_FILTER_USE_BOTH
);

// 按参数名升序排列
ksort($filtered);

// 签名时使用原始值,不提前进行 URL 编码
$signContent = http_build_query($filtered, '', '&', PHP_QUERY_RFC3986);

// http_build_query 会编码 URL,不能直接作为旧版 MD5 接口的签名原文。
// 因此显式重建未编码的 key=value 字符串。
$pairs = [];

foreach ($filtered as $name => $value) {
    $pairs[] = $name . '=' . $value;
}

$signContent = implode('&', $pairs);
$sign = md5($signContent . $md5Key);

// 签名完成后再添加这两个字段
$params['sign_type'] = 'MD5';
$params['sign'] = $sign;

$requestUrl = 'https://mapi.alipaydev.com/gateway.do?'
    . http_build_query($params, '', '&', PHP_QUERY_RFC3986);

echo "Sign content:\n" . $signContent . "\n\n";
echo "Sign:\n" . $sign . "\n\n";
echo "Request URL:\n" . $requestUrl . "\n";

重点检查项

1. 测试账号必须与测试网关匹配

如果请求发送到:

https://mapi.alipaydev.com/gateway.do

那么 partner 和 MD5 key 也必须来自同一个测试环境账号。以下信息不能混用:

  • 正式环境的 partner
  • 正式环境的 MD5 key
  • 另一个测试账号的密钥
  • RSA 私钥或公钥
  • 支付宝登录密码

partner 只是商户身份标识,不是签名密钥。

2. 不要使用编码后的 URL 计算签名

签名时应使用:

notify_url=http://www.alipay.com

而不是:

notify_url=http%3A%2F%2Fwww.alipay.com

URL 编码要在签名计算完成后进行。否则,支付宝解码参数并复算签名时,使用的原文可能与商户签名时使用的原文不同。

3. 实际发送的参数必须全部参与签名

除了 sign、sign_type 和空值,实际发送的参数一般都要参与签名,包括:

_input_charset
sendFormat
service
partner
currency
total_fee

生成签名后,不要再增加、删除或修改参数。

4. 参数名区分大小写

以下参数名必须保持接口规定的拼写和大小写:

_input_charset
out_trade_no
total_fee
sign_type

sendFormat 是否是该接口的有效参数,需要根据当前测试账号对应的接口文档确认。如果请求中包含这个参数,签名原文中也必须使用完全相同的名称和值。

5. 金额应作为字符串处理

不要让浮点数格式化过程将:

2.2

自动转换为:

2.20

也要避免产生二进制浮点误差。签名原文中的金额字符串必须与最终请求里的值逐字一致。

6. 打印签名原文并逐项比对

排查时,不要只打印最终 URL,还应记录以下内容:

排序后的参数列表
最终签名原文
签名结果
实际发送的参数
请求所用网关
partner

不要将 MD5 key 写入日志。需要逐项确认签名原文中的值与支付宝实际收到的值一致。

如果确认上述算法无误后仍然返回 ILLEGAL_SIGN,最可能的原因是测试环境的 partner 与 MD5 key 不匹配、密钥尚未正确配置,或者该账号实际要求的签名类型不是 MD5。此时应在测试商户后台重新确认签名方式和密钥,并使用后台为该测试账号生成或分配的对应凭据。

备注:内容仅供参考。