PAYATHON 2026

支付宝当面付扫码支付无反应并出现白屏是什么原因?

支付小周

结论

支付宝当面付出现“能扫码,但随后白屏或页面没有反应”,问题通常不在二维码显示组件,而在支付流程中的某个环节。程序没有正确处理失败结果,也没有记录相关错误,就容易只剩下一片白屏。

先区分两种情况:

  • 支付宝客户端扫码后白屏,通常与二维码内容错误、预下单失败、接口返回值未经校验,或服务端异常被隐藏有关。
  • 付款后网站支付页没有变化,是因为当面付的异步通知不会自动刷新浏览器。网站需要处理 notify_url,再通过轮询或 WebSocket 获取订单状态。

除了核对 App ID、密钥等后台配置,还要检查预下单响应、服务器错误日志、异步通知和前端订单状态更新。

先确认白屏出现在哪里

排查前,先确定白屏是在手机支付宝中出现,还是电脑浏览器中的支付页面没有变化。

支付宝客户端扫码后白屏

如果手机支付宝扫码后出现白屏,先检查二维码实际编码的内容。二维码必须使用 alipay.trade.precreate 接口成功响应中的 qr_code 生成,不能把支付页面地址、空字符串或错误信息直接编码成二维码。

可以临时记录二维码的原始内容:

error_log('Alipay QR content: ' . $qrCode);

正常情况下,这里应该记录支付宝返回的支付地址。如果日志内容为空、本站 URL、HTML 错误页面或 JSON 错误信息,说明程序在生成二维码之前就已经失败。

付款成功,但电脑网页不变化

notify_url 是支付宝服务器请求网站服务器的地址,它不会直接通知用户当前打开的浏览器页面。

完整流程通常如下:

  1. 网站调用 alipay.trade.precreate 创建订单。
  2. 页面显示接口返回的 qr_code。
  3. 用户在支付宝中完成付款。
  4. 支付宝服务器向 notify_url 发送异步通知。
  5. 网站验签,并将本地订单更新为已支付。
  6. 支付页面定时查询本地订单状态,发现已支付后跳转。

如果没有第 6 步,即使后台已经收到款,网页也可能一直停在二维码页面。

检查预下单接口的真实响应

不能只确认 SDK 调用是否执行,还要检查支付宝返回的 code、sub_code、sub_msg 和 qr_code。

下面的示例采用常见的 PHP 支付宝 SDK 写法。不同 SDK 版本的类名和响应结构可能略有差异:

<?php

$aop = new AopClient();
$aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
$aop->appId = $appId;
$aop->rsaPrivateKey = $merchantPrivateKey;
$aop->alipayrsaPublicKey = $alipayPublicKey;
$aop->signType = 'RSA2';
$aop->format = 'json';
$aop->charset = 'UTF-8';

$request = new AlipayTradePrecreateRequest();

$bizContent = [
    'out_trade_no' => $orderNo,
    'total_amount' => number_format($amount, 2, '.', ''),
    'subject'       => $subject,
];

$request->setBizContent(
    json_encode($bizContent, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)
);
$request->setNotifyUrl('https://example.com/payment/alipay/notify');

try {
    $result = $aop->execute($request);
    $response = $result->alipay_trade_precreate_response ?? null;

    if (!$response) {
        throw new RuntimeException('支付宝预下单响应结构异常');
    }

    if (($response->code ?? '') !== '10000') {
        $message = sprintf(
            '预下单失败:code=%s, msg=%s, sub_code=%s, sub_msg=%s',
            $response->code ?? '',
            $response->msg ?? '',
            $response->sub_code ?? '',
            $response->sub_msg ?? ''
        );

        error_log($message);
        throw new RuntimeException($message);
    }

    $qrCode = $response->qr_code ?? '';

    if ($qrCode === '') {
        throw new RuntimeException('支付宝返回成功,但 qr_code 为空');
    }

    // 使用 $qrCode 生成二维码
} catch (Throwable $e) {
    error_log(sprintf(
        '[Alipay] %s in %s:%d',
        $e->getMessage(),
        $e->getFile(),
        $e->getLine()
    ));

    http_response_code(500);
    exit('支付订单创建失败,请稍后重试');
}

使用沙箱环境时,App ID、支付宝公钥、网关地址和客户端都必须属于沙箱。沙箱配置不能与正式环境配置混用。

查看服务器错误,不要依赖白屏提示

白屏往往表示 PHP 发生了致命错误,只是生产环境关闭了错误显示。常见原因包括:

  • 支付 SDK 没有正确加载;
  • 缺少所需的 PHP 扩展;
  • 类名或命名空间错误;
  • 私钥文件读取失败;
  • 返回对象为空,程序却继续读取其属性;
  • 输出 JSON 前混入了警告信息;
  • 框架将异常转换成了空响应;
  • 文件权限、缓存或运行目录配置不正确。

排查时,优先查看这些信息:

  • PHP-FPM 错误日志;
  • Nginx 或 Apache 错误日志;
  • 网站框架的应用日志;
  • 浏览器开发者工具中的 Network 和 Console;
  • 支付接口请求对应的 HTTP 状态码和响应正文。

开发环境可以临时开启 PHP 错误显示:

ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);

生产环境不要长期开启错误显示,否则可能泄露目录、密钥路径和数据库信息等敏感内容。页面应关闭错误显示,并将异常写入受保护的日志。

正确处理支付宝异步通知

notify_url 必须是支付宝服务器能够从公网访问的 HTTPS 地址,不能使用以下地址:

  • localhost;
  • 127.0.0.1;
  • 局域网 IP;
  • 登录后才能访问的地址;
  • 会被防火墙或安全插件拦截的路径;
  • 返回 301 或 302 跳转的地址。

通知处理至少需要完成这些工作:

  1. 使用支付宝公钥验签。
  2. 校验 app_id。
  3. 根据 out_trade_no 查找本地订单。
  4. 校验订单金额和收款方信息。
  5. 只在交易状态有效时更新订单。
  6. 处理完成后仅返回纯文本 success。
  7. 确保重复通知不会造成重复入账。

示意代码如下:

<?php

$postData = $_POST;

if (!$aop->rsaCheckV1($postData, null, 'RSA2')) {
    http_response_code(400);
    exit('failure');
}

if (($postData['app_id'] ?? '') !== $expectedAppId) {
    http_response_code(400);
    exit('failure');
}

$orderNo = $postData['out_trade_no'] ?? '';
$tradeNo = $postData['trade_no'] ?? '';
$status   = $postData['trade_status'] ?? '';
$amount   = $postData['total_amount'] ?? '';

$order = findOrderByOrderNo($orderNo);

if (!$order) {
    exit('failure');
}

if (bccomp((string) $order['amount'], (string) $amount, 2) !== 0) {
    exit('failure');
}

if (in_array($status, ['TRADE_SUCCESS', 'TRADE_FINISHED'], true)) {
    // 需要使用事务或唯一约束保证幂等,避免重复通知造成重复入账。
    markOrderPaid($orderNo, $tradeNo);
}

exit('success');

rsaCheckV1 的参数形式可能因 SDK 版本而异,应以当前项目安装的官方 SDK 接口为准。验签失败时不能更新订单,也不能只根据前端提交的“支付成功”修改订单状态。

让支付页面主动查询订单状态

异步通知更新数据库后,支付页面可以每隔两三秒查询一次本站的订单状态:

const timer = setInterval(async () => {
  try {
    const response = await fetch(
      `/payment/status?order_no=${encodeURIComponent(orderNo)}`,
      {
        method: 'GET',
        credentials: 'same-origin',
        cache: 'no-store'
      }
    );

    if (!response.ok) {
      return;
    }

    const result = await response.json();

    if (result.paid === true) {
      clearInterval(timer);
      window.location.href =
        `/payment/success?order_no=${encodeURIComponent(orderNo)}`;
    }
  } catch (error) {
    console.error('查询支付状态失败', error);
  }
}, 3000);

状态接口只能读取服务器数据库中的支付结果,不能相信浏览器自行提交的支付状态。接口还要验证订单是否属于当前登录用户,以免泄露其他订单的信息。

如果异步通知暂时没有到达,服务端也可以调用 alipay.trade.query 主动查询支付宝订单状态,但要设置合理的查询频率和超时时间,不能让前端无限高频调用支付宝接口。

按这个顺序排查

  1. 确认扫码后的白屏出现在支付宝客户端,还是网站浏览器。
  2. 记录 alipay.trade.precreate 返回的完整错误字段,确认 code 为 10000。
  3. 确认二维码使用的数据确实是响应中的 qr_code。
  4. 查看 PHP、Web 服务器和框架日志,找到与白屏对应的异常。
  5. 在浏览器 Network 面板中检查接口是否返回 500、空内容或无效 JSON。
  6. 确认沙箱环境和正式环境的配置没有混用。
  7. 检查应用私钥、支付宝公钥和 RSA2 签名配置。
  8. 从公网测试 notify_url 是否可以访问,并确认该地址没有登录验证或重定向。
  9. 查看通知日志和验签结果,确认本地订单是否已更新。
  10. 为支付页面增加订单状态轮询,在付款成功后跳转到成功页面。

注意事项

  • 不要在日志中完整记录应用私钥、签名值、用户隐私数据或支付凭证。
  • 金额要使用字符串或高精度十进制处理,不要直接使用浮点数比较。
  • out_trade_no 必须唯一,同一订单重复提交时也要保证幂等。
  • 支付是否成功,应以验签通过的支付宝通知或服务端主动查询结果为准。
  • “没有提示”不能作为有效的错误信息。先记录预下单响应和异常,通常很快就能确定问题出在配置、代码、网络回调,还是前端缺少状态更新逻辑。

备注:内容仅供参考。