升级支付API是否影响旧系统并导致ILLEGLE_SIGN错误
结论
ILLEGLE_SIGN(部分接口实际返回 ILLEGAL_SIGN)说明服务端未能通过签名校验,但不能据此判断 create_direct_pay_by_user 已废弃。常见原因包括签名类型、密钥、字符集、参数排序错误,或者请求网关与接入模式不匹配。
create_direct_pay_by_user 是较早的网页即时到账接入模式,通常使用商户合作伙伴身份和旧式签名方案。新的开放平台 API 通常通过 app_id 标识应用,并使用 RSA/RSA2 签名。两种模式的参数结构和密钥体系不同,不能通过添加 app_id 完成升级,也不能直接沿用旧接口的签名代码。
接入新 API 需要可用的应用和 app_id,但未必需要重新创建应用:
- 已有开放平台应用,且具备相应支付能力,可以评估后继续使用。
- 只有旧商户号或合作伙伴身份,没有可用应用时,需要创建并配置应用。
- 单独新建应用通常不会影响现有手机端支付。
- 修改手机端现用应用的密钥、签名类型、回调配置或支付产品权限,可能导致手机端支付失败。
为什么会出现 ILLEGLE_SIGN
签名类型与接口模式不匹配
旧接口可能使用 MD5、RSA 等历史签名方式,新开放平台接口一般使用 RSA2。如果请求中声明:
sign_type=RSA2
实际签名却由 MD5 或 SHA1withRSA 生成,服务端无法通过验签。反过来也一样,请求声明 MD5,实际却用 RSA 私钥签名,同样会失败。
手机端可以正常支付,只能说明它当前使用的接口、密钥和签名方式彼此匹配,不能证明网页端的签名没有问题。手机端和网页端可能使用不同的接口、应用、合作伙伴身份或网关。
混用了两套密钥体系
排查时需要分清:
- 旧接口的商户合作伙伴身份和安全校验码
- 开放平台应用的
app_id - 应用私钥
- 应用公钥
- 平台公钥或平台证书
常见错误有:
- 使用平台公钥为请求签名
- 使用应用公钥代替应用私钥
- 使用了另一个应用的私钥
app_id和应用私钥不属于同一个应用- 平台配置的应用公钥与本地私钥不成对
- 在正式环境中使用沙箱密钥,或在沙箱环境中使用正式密钥
商户侧使用应用私钥生成签名,平台公钥主要用于验证平台返回的数据,两者不能互换。
待签名字符串构造错误
旧接口通常要求先过滤、排序,再拼接业务参数。下面任何一处不一致都可能导致验签失败:
- 把
sign本身放入待签名参数 - 错误包含或排除了
sign_type - 双方处理空值参数的方式不同
- 参数没有按规定排序
- 签名前后的 URL 编码次数不同
+、%、空格、中文或换行发生转换- JSON 被重新格式化,导致字段顺序或转义方式改变
- 使用 GBK 生成字节,却声明
utf-8 - 表单提交前又修改了已经签名的值
签名校验针对具体的字节内容。两个字符串即使在页面上看起来相同,只要编码、空格或换行不同,签名结果就可能不同。
请求网关与参数版本不一致
升级时很容易把旧参数发往新网关,或者把新参数发往旧网关。例如:
- 请求中仍有
service=create_direct_pay_by_user - 同时加入了新 API 的
app_id、method、version、biz_content - 使用新应用私钥签署旧接口参数
- 使用旧安全校验码签署开放平台 API 请求
旧接口和新 API 不是简单的参数增补关系。两者需要分别按照各自的文档构造完整请求。
建议的排查步骤
1. 确认实际返回的错误码
先记录支付平台返回的原始错误码、子错误码和请求标识,确认实际返回的是:
ILLEGLE_SIGN
还是:
ILLEGAL_SIGN
不要只看页面上的中文提示,前端可能对原始错误做过二次包装。
2. 固定当前接入链路
先确定网页端使用的是哪套模式:
旧模式:
service=create_direct_pay_by_user
partner=...
sign_type=...
或者:
开放平台模式:
app_id=...
method=alipay.trade.page.pay
sign_type=RSA2
version=...
biz_content=...
同一次请求不能混用两套模式。
3. 核对签名身份
逐项检查以下对应关系:
请求 app_id
↕
平台后台中的应用
↕
该应用配置的应用公钥
↕
服务器实际使用的应用私钥
只要其中一项属于其他应用,即使签名代码没有问题,也会返回签名错误。
4. 输出待签名原文
在测试环境中记录:
接口名称
网关地址
app_id 或 partner
sign_type
charset
排序后的待签名字符串
签名结果
最终提交参数
不要把私钥、安全校验码和完整的敏感业务数据写入日志。排查时要对比“签名前的原文”和“实际发送的参数”,不能只比较签名结果。
5. 使用平台 SDK 交叉验证
如果手写签名一直失败,可以使用平台当前提供的官方 SDK,根据同一组参数生成一次请求:
- SDK 请求成功,问题很可能出在自定义签名或编码逻辑中。
- SDK 同样失败,需要继续检查应用状态、密钥配置、支付产品权限和运行环境。
- 不要把示例中的
app_id、密钥或网关直接用于生产环境。
旧接口签名检查示例
下面只演示旧式参数签名的检查思路。是否需要包含 sign_type、使用哪种摘要算法以及采用什么字符集,必须以当前账户对应的旧接口文档为准。
<?php
function buildLegacySignContent(array $params): string
{
unset($params['sign'], $params['sign_type']);
$params = array_filter(
$params,
static fn ($value) => $value !== null && $value !== ''
);
ksort($params, SORT_STRING);
$pairs = [];
foreach ($params as $key => $value) {
// 不要在这里对 value 进行 urlencode,除非对应接口明确要求。
$pairs[] = $key . '=' . $value;
}
return implode('&', $pairs);
}
$params = [
'service' => 'create_direct_pay_by_user',
'partner' => 'YOUR_PARTNER_ID',
'_input_charset' => 'utf-8',
'out_trade_no' => 'ORDER_202609150001',
'subject' => '测试订单',
'payment_type' => '1',
'total_fee' => '0.01',
];
$content = buildLegacySignContent($params);
// 旧接口如果明确要求 MD5,其计算形式通常还会涉及商户安全校验码。
// 不要根据此示例猜测规则,应严格采用对应接口文档或原有 SDK。
error_log('legacy sign content: ' . $content);
这个示例用于检查参数过滤、排序、编码和最终提交内容是否一致,不建议以此为依据重新实现生产支付签名。
新 API 接入示例
新的网页支付通常应使用开放平台 SDK,由 SDK 组织公共参数、完成签名并生成表单。下面只展示代码结构,类名和属性应以实际安装的 SDK 版本为准:
<?php
$aop = new AopClient();
$aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
$aop->appId = 'YOUR_APP_ID';
$aop->rsaPrivateKey = file_get_contents('/secure/path/app_private_key.pem');
$aop->alipayrsaPublicKey = file_get_contents('/secure/path/alipay_public_key.pem');
$aop->signType = 'RSA2';
$aop->postCharset = 'UTF-8';
$aop->format = 'json';
$request = new AlipayTradePagePayRequest();
$request->setBizContent(json_encode([
'out_trade_no' => 'ORDER_202609150001',
'product_code' => 'FAST_INSTANT_TRADE_PAY',
'total_amount' => '0.01',
'subject' => '测试订单',
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
$request->setReturnUrl('https://www.example.com/payment/return');
$request->setNotifyUrl('https://www.example.com/payment/notify');
$form = $aop->pageExecute($request);
echo $form;
生产环境中的私钥应保存在受控的密钥服务或服务器安全目录中,不能写入网页代码、前端 JavaScript、代码仓库或日志。
新接入是否会影响手机端
如果新建网页支付应用,同时保留手机端的原有配置,两套接入通常可以并行运行:
现有手机端
→ 原应用或原商户配置
→ 原接口和原密钥
新网页端
→ 新应用 app_id
→ 新 API 和新应用私钥
需要避免:
- 覆盖手机端正在使用的应用公钥
- 替换服务端共用的私钥文件
- 把全局
app_id直接改为网页端的新app_id - 修改共用回调地址后,没有区分移动端和网页端订单
- 让两个应用共用一套配置变量,导致运行时读取错误的密钥
- 未经验证就停用旧应用或旧支付产品
较稳妥的做法是按支付渠道分别保存配置:
PAY_MOBILE_APP_ID
PAY_MOBILE_PRIVATE_KEY
PAY_MOBILE_PLATFORM_PUBLIC_KEY
PAY_WEB_APP_ID
PAY_WEB_PRIVATE_KEY
PAY_WEB_PLATFORM_PUBLIC_KEY
还应在测试环境中分别完成下单、支付、同步跳转、异步通知验签、退款和订单查询测试,再逐步切换网页端流量。
create_direct_pay_by_user 在当前账户和平台中能否继续使用,应以平台后台显示的产品状态、合同权限和当前官方文档为准。即使旧接口暂时仍可调用,也建议把网页端迁移到当前支持的开放平台支付 API。迁移需要按独立接入处理,不能只修改旧请求中的几个参数。
备注:内容仅供参考。
