PAYATHON 2026

旧版 create_direct_pay_by_user 接口偶发“钓鱼风险”报错

支付小周

结论

这种问题只影响部分用户,偶尔发生,而且重试后可以恢复,通常不是固定参数或签名错误。更可能的原因是支付宝风控系统根据当次交易、用户账户、网络环境、设备状态和商户历史行为,临时拦截了请求。

create_direct_pay_by_user 是旧版网站支付接口。即使商户长期没有修改请求参数,支付宝侧的风控策略、产品权限或兼容性仍可能变化。排查时可以同时处理两个方向:

  1. 保存异常请求和正常请求的完整上下文,比较两者差异。
  2. 检查商品信息、金额、订单号、域名和跳转链路。
  3. 请用户更换网络或浏览器,进行交叉验证。
  4. 携带异常时间、商户号和订单号,联系支付宝技术支持查询风控记录。
  5. 确认当前签约产品支持的新版接口,并制定迁移方案。

为什么重试后又能正常访问

如果签名、商户号或必填参数有确定性错误,同一个请求通常会一直失败,不会只影响部分用户,也不会在简单重试后恢复。

偶发拦截可能与以下因素有关。

用户账户或设备触发风控

支付宝可能会结合用户账户状态、登录环境、设备指纹、浏览器特征和访问频率进行判断。例如:

  • 用户频繁切换账户或设备。
  • 浏览器禁用了 Cookie、JavaScript,或者开启了严格隐私模式。
  • 用户正在使用代理、VPN、远程桌面或异常浏览器环境。
  • 短时间内多次创建金额相同的订单。
  • 同一设备登录了多个支付宝账户。
  • 用户账户正处于临时安全校验状态。

如果用户切换网络、浏览器或设备后恢复,问题通常与用户环境或当次风控判断有关。

出口 IP 或网络链路异常

部分用户可能通过公司代理、公共 Wi-Fi、运营商共享出口或海外网络访问。某些出口 IP 的风险评分较高,支付宝页面可能因此显示安全提示。

还要检查支付跳转前是否经过以下环节:

  • 短链接服务。
  • 第三方统计或广告跳转。
  • 非 HTTPS 中间页面。
  • 多次 302 重定向。
  • iframe、WebView 或内嵌浏览器。
  • 会修改查询参数的 CDN、WAF 或反向代理。

支付请求最好直接提交到支付宝官方网关,尽量减少不必要的中转地址。

订单或商品信息命中风险规则

subject、body、金额以及订单生成规律也可能影响风险判断。需要重点检查:

  • 商品名称是否包含网址、联系方式、二维码、充值代充、返现等敏感表达。
  • 不同用户的 subject 或 body 是否直接拼接了用户输入。
  • 金额是否集中在特殊小额,或短时间内反复出现。
  • out_trade_no 是否真正唯一。
  • 失败重试时是否重复创建了多个订单。
  • 商品描述是否为空、乱码、被截断或含有不可见字符。
  • 字符集是否始终与请求中的 _input_charset 一致。

不要只比较代码配置,还要比较最终发送给支付宝的实际参数。

商户域名或页面链路存在异常

检查 return_url、notify_url 和支付发起页面时,应确认:

  • 域名是否由商户实际控制。
  • HTTPS 证书是否有效,证书链是否完整。
  • 是否存在 HTTP 和 HTTPS 之间的反复跳转。
  • 是否跳转到与业务无关的域名。
  • 页面是否加载了被篡改的脚本、广告或第三方资源。
  • DNS、CDN 或反向代理是否在不同地区返回不同结果。
  • 不同用户是否被灰度到不同的支付节点或参数配置。

如果系统使用多机房、多实例或灰度发布,还要确认所有节点使用相同的商户配置、字符集和签名密钥。

建议的排查步骤

1. 为每次支付生成链路追踪号

记录支付创建时间、内部订单号、支付宝商户号、用户环境和最终请求参数。不要在日志中明文保存敏感字段,尤其不能记录签名值和密钥。

建议至少记录:

  • 内部追踪号。
  • out_trade_no。
  • partner。
  • service。
  • payment_type。
  • seller_id 或 seller_email。
  • total_fee。
  • subject 的原始值及摘要。
  • return_url、notify_url 的主机名。
  • 参数字符集。
  • 请求发起节点。
  • 客户端 IP、出口 IP 和 User-Agent。
  • 用户看到报错的准确时间和截图。
  • 支付宝页面显示的错误码、请求号或流水标识。

客户端 IP 属于敏感信息,应按照隐私和日志留存规范进行脱敏。

2. 对比同一用户的失败请求和成功请求

除了业务订单数据,还要比较最终提交的参数,重点关注:

  • 重试时 out_trade_no 是否变化。
  • subject、body、total_fee 是否变化。
  • 请求是否由不同的应用实例发起。
  • 参数编码和排序是否一致。
  • 用户使用的网络、浏览器和入口页面是否变化。
  • 请求是否经过不同的 CDN 或代理节点。

如果参数完全相同,但第一次被拦截,随后又成功,通常需要支付宝根据后台请求记录进一步确认。商户很难只凭页面提示判断具体命中了哪条风控规则。

3. 做用户环境交叉测试

在不诱导用户绕过安全校验的前提下,可以请受影响的用户尝试:

  • 使用系统浏览器打开,不使用应用内 WebView。
  • 关闭代理或 VPN。
  • 在移动网络和 Wi-Fi 之间切换。
  • 清理支付宝及浏览器缓存,然后重新登录。
  • 更换一台可信设备测试。
  • 确认设备的时间和时区正确。

如果更换网络后立即恢复,应重点检查出口 IP 和代理链路。如果更换账户后恢复,则更可能与账户级风控有关。

4. 检查服务端是否存在多套配置

偶发问题也可能来自某个应用节点的配置不一致,例如:

  • 部分节点仍在使用旧密钥。
  • 不同节点的字符集配置不同。
  • partner 与收款账户不匹配。
  • 某个节点生成了不同的回调域名。
  • 部分节点的系统时间异常。
  • 网关地址被环境变量覆盖。
  • 参数经过了二次 URL 编码。

可以在日志中加入应用实例标识和配置版本,但不要记录私钥或合作伙伴密钥。

参数记录示例

下面的 PHP 示例仅用于说明如何生成便于比较的安全日志,不是完整的支付宝支付实现。实际签名逻辑应继续使用项目中已经验证的实现,或支付宝提供的 SDK。

<?php

function maskPartner(string $partner): string
{
    if (strlen($partner) <= 6) {
        return '***';
    }

    return substr($partner, 0, 3) . '***' . substr($partner, -3);
}

function paymentLogContext(array $params, string $traceId): array
{
    $returnHost = isset($params['return_url'])
        ? parse_url($params['return_url'], PHP_URL_HOST)
        : null;

    $notifyHost = isset($params['notify_url'])
        ? parse_url($params['notify_url'], PHP_URL_HOST)
        : null;

    return [
        'trace_id'       => $traceId,
        'service'        => $params['service'] ?? null,
        'partner'        => isset($params['partner'])
            ? maskPartner((string) $params['partner'])
            : null,
        'out_trade_no'   => $params['out_trade_no'] ?? null,
        'payment_type'   => $params['payment_type'] ?? null,
        'total_fee'      => $params['total_fee'] ?? null,
        'subject_sha256' => isset($params['subject'])
            ? hash('sha256', (string) $params['subject'])
            : null,
        'subject_length' => isset($params['subject'])
            ? mb_strlen((string) $params['subject'], 'UTF-8')
            : null,
        'return_host'    => $returnHost,
        'notify_host'    => $notifyHost,
        'charset'        => $params['_input_charset'] ?? null,
    ];
}

$params = [
    'service'        => 'create_direct_pay_by_user',
    'partner'        => 'YOUR_PARTNER_ID',
    '_input_charset' => 'utf-8',
    'payment_type'   => '1',
    'out_trade_no'   => $orderNo,
    'subject'        => $subject,
    'total_fee'      => $amount,
    'return_url'     => 'https://pay.example.com/alipay/return',
    'notify_url'     => 'https://pay.example.com/alipay/notify',
];

$traceId = bin2hex(random_bytes(16));

error_log(json_encode(
    paymentLogContext($params, $traceId),
    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
));

日志中不应保存以下内容:

商户私钥
支付宝公钥之外的密钥材料
旧接口的 MD5 key
完整 sign 值
用户登录凭证
未经脱敏的身份证号、手机号或银行卡号

如何向支付宝提交排查信息

“钓鱼风险”属于安全提示,具体命中了哪条规则,通常只能由支付宝结合后台记录确认。提交工单时,建议一次性提供:

  • 商户号或 partner。
  • 接口名称 create_direct_pay_by_user。
  • 异常订单的 out_trade_no。
  • 异常发生的准确时间,并注明时区。
  • 报错页面的完整截图。
  • 页面显示的错误码、请求号或流水标识。
  • 同一用户随后成功订单的编号和时间。
  • 用户是否更换过网络、浏览器或设备。
  • 支付发起域名,以及 return_url 和 notify_url 的域名。
  • 异常请求与成功请求的参数差异,其中应删除签名和敏感信息。

不要为了测试而反复、高频创建订单,这种操作本身也可能继续触发风控。

解决建议

如果最终确认是用户或网络环境触发了临时风控,业务侧应提供清楚的失败提示和重新发起入口,但不能尝试绕过支付宝安全页面,也不要自动无限重试。

如果问题集中在特定参数、域名或节点,应修复参数生成方式和跳转链路,并确保所有应用实例的配置一致。

还应向支付宝确认当前商户签约产品对应的现行网站支付接口和迁移要求。升级不能只替换 service 名称。新版接口的签名算法、请求格式、证书或密钥配置、异步通知验签方式和订单查询方式都可能不同,必须按照当前签约产品的官方接入文档重新联调。

在支付宝提供后台记录之前,无法只凭“钓鱼风险”四个字确定唯一原因。应保留失败请求与成功请求的成对证据,再由支付宝根据发生时间、商户号和订单号查询对应的风控记录。

备注:内容仅供参考。