支付宝当面付二维码突然无法加载,如何解决?
结论
二维码突然无法加载,通常不是“当面付二维码失效”,而是以下两个环节之一出了问题:
- 服务端调用
alipay.trade.precreate失败,未获得有效的qr_code。 - 服务端已经获得
qr_code,但第三方二维码图片接口、前端脚本或浏览器安全策略导致图片无法显示。
排查时,先查看支付宝接口的原始响应,再检查浏览器控制台和网络请求,不要只看页面上的“图片加载失败”。
第一步:确认接口是否返回 qr_code
当面付预创建接口调用成功后,响应中应包含 qr_code。这个字段保存的是二维码内容,不一定能直接作为 <img src> 的图片地址。
建议临时记录以下字段:
code
msg
sub_code
sub_msg
out_trade_no
qr_code
PHP 示例:
$response = $aopClient->execute($request);
$result = $response->alipay_trade_precreate_response ?? null;
if (!$result) {
error_log('支付宝响应结构异常:' . json_encode(
$response,
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
));
throw new RuntimeException('支付创建失败,请稍后重试');
}
if (($result->code ?? '') !== '10000') {
error_log(sprintf(
'支付宝预创建失败:code=%s, msg=%s, sub_code=%s, sub_msg=%s',
$result->code ?? '',
$result->msg ?? '',
$result->sub_code ?? '',
$result->sub_msg ?? ''
));
throw new RuntimeException('支付创建失败,请检查支付宝接口日志');
}
$qrCode = $result->qr_code ?? '';
if ($qrCode === '') {
throw new RuntimeException('支付宝接口成功,但未返回 qr_code');
}
不同版本的支付宝 SDK 可能使用不同的响应对象结构,具体应以项目实际安装的版本为准。排查时,最好记录脱敏后的原始响应,不要只判断请求有没有抛出异常。
第二步:不要把 qr_code 直接当作图片地址
下面的写法有问题:
<img src="<?= htmlspecialchars($qrCode, ENT_QUOTES, 'UTF-8') ?>">
qr_code 是需要编码进二维码的数据,不能保证对应地址会返回图片。应使用二维码库把这段内容转换成二维码图片。
前端生成二维码
先在页面中引入本地部署的二维码库,再生成二维码:
<div id="payment-qrcode"></div>
<script>
const qrCodeContent = <?= json_encode(
$qrCode,
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
) ?>;
if (!qrCodeContent) {
throw new Error('二维码内容为空');
}
new QRCode(document.getElementById('payment-qrcode'), {
text: qrCodeContent,
width: 220,
height: 220,
correctLevel: QRCode.CorrectLevel.M
});
</script>
构造方式和参数名称取决于实际使用的二维码库。上面的代码适用于常见的 QRCode 接口。
PHP 服务端生成二维码
如果项目通过 Composer 管理依赖,可以使用二维码组件在服务端生成图片。下面只演示处理方式,具体类名和方法应以已安装的组件版本为准:
header('Content-Type: image/png');
$qrCode = getQrCodeFromAlipay();
if ($qrCode === '') {
http_response_code(500);
exit;
}
// 使用项目中已经安装的二维码组件生成 PNG。
// 不要在这里请求不受控制的第三方“二维码生成接口”。
echo generateQrCodePng($qrCode);
生产环境更适合使用本地二维码组件,以免第三方服务停机、限流、修改接口或被浏览器拦截。
第三步:检查是否依赖了第三方二维码接口
不少旧项目通过下面的方式显示二维码:
<img src="http://example-qrcode-service.com/create?text=...">
这种方案可能突然失效,常见原因包括:
- 第三方接口停止服务或更换了路径;
- 接口开始要求鉴权;
- 请求频率受到限制;
- HTTPS 页面加载 HTTP 图片,被浏览器作为混合内容拦截;
- 二维码内容没有经过 URL 编码;
- 第三方域名的证书、DNS 或网络异常;
- 二维码内容拼接到 URL 后超过长度限制。
如果暂时必须使用图片接口,至少要正确编码参数:
$imageUrl = 'https://example.com/qrcode?text=' . rawurlencode($qrCode);
这只能解决编码问题,无法处理第三方服务自身的不稳定。更可靠的办法还是在自己的网站中生成二维码。
第四步:查看浏览器控制台和网络请求
打开浏览器开发者工具,检查以下内容:
Console中是否有Mixed Content错误;- 二维码脚本是否返回
404或403; - 图片请求是否返回
5xx; - 是否出现
Content Security Policy拦截; - JavaScript 是否报出
QRCode is not defined; - 接口返回的
qr_code是否为空; - 前端支付接口是否因登录失效、跨域或网关错误而失败。
如果网站最近启用了 HTTPS,而二维码图片或脚本仍然使用 HTTP 地址,应统一改为 HTTPS:
<!-- 容易被 HTTPS 页面拦截 -->
<script src="http://cdn.example.com/qrcode.min.js"></script>
<!-- 应改为 HTTPS,或部署到本站 -->
<script src="/assets/js/qrcode.min.js"></script>
如果网站配置了 CSP,还要允许实际使用的脚本、图片或网络请求来源。例如,本地生成的二维码可能使用 data: 图片:
Content-Security-Policy: default-src 'self'; img-src 'self' data: blob:
不要直接照搬这段 CSP 配置。应结合网站现有策略调整,避免无意中放宽安全限制。
第五步:排查支付宝接口配置
如果 alipay.trade.precreate 调用失败,应根据 sub_code 和 sub_msg 定位原因,而不是反复更换二维码插件。需要检查:
- 应用是否仍处于可用状态;
- 当面付产品及相关权限是否正常;
app_id是否与密钥、证书和网关环境匹配;- 应用私钥、支付宝公钥或证书是否更换过;
- SDK 中配置的是支付宝公钥还是应用公钥;
- 签名算法和字符集是否与现有配置一致;
- 服务器时间是否明显不准确;
- 服务器能否正常访问支付宝网关;
out_trade_no是否重复;- 金额、商品标题等业务参数是否符合接口要求;
- 生产环境和沙箱环境是否混用了配置。
证书模式与普通公钥模式的配置方式不同。即使项目原来一直正常运行,也可能因证书到期、配置文件被覆盖、服务器迁移或运行环境升级而出现故障。
第六步:检查近期变更
如果功能此前长期正常,应先回顾故障发生前后是否有这些变化:
- 是否升级了 PHP、Java、Node.js 或支付宝 SDK;
- 是否更换了服务器、域名、CDN、WAF 或反向代理;
- 是否启用了全站 HTTPS;
- 是否修改了 CSP、安全响应头或跨域配置;
- 是否调整过支付宝开放平台的密钥、证书或应用设置;
- 是否替换了二维码生成服务;
- 服务器磁盘是否写满,缓存目录是否变得不可写;
- 是否更新了前端依赖,导致二维码库未能成功加载。
这类“突然坏掉”的问题,通常与近期的环境变化有关,并非业务代码无缘无故失效。
建议的完整处理流程
建议按以下顺序排查:
- 在服务端记录
alipay.trade.precreate的脱敏响应。 - 确认响应中的
code是否为10000。 - 确认
qr_code是否存在且不为空。 - 如果没有
qr_code,根据sub_code和sub_msg排查接口配置及业务参数。 - 如果存在
qr_code,检查前端是否误把它当成图片地址。 - 检查浏览器控制台中的混合内容、CSP、脚本加载和网络错误。
- 移除第三方二维码生成接口,改用本地二维码库。
- 使用支付宝客户端实际扫码,确认订单金额和商户信息正确。
注意事项
不要把应用私钥、支付宝公钥、证书内容、完整签名参数或用户信息输出到页面或公开日志中。记录接口响应时,应对敏感字段进行脱敏。
二维码只承载支付链接,不能证明订单已经支付。支付结果应以后端异步通知或主动查询接口为准,同时完成签名验证、金额核对、商户身份核对和订单状态的幂等处理。
不要重复使用已经关闭、过期或支付完成的订单号。用户刷新支付页面时,最好先检查现有订单状态,再决定继续展示原二维码还是创建新订单。
备注:内容仅供参考。
