PAYATHON 2026

支付宝沙箱支付突然要求验证码且图片无法显示

支付阿杰

结论

支付宝沙箱支付突然要求输入验证码,但验证码图片无法显示,问题通常不在同步回调代码。更常见的原因是沙箱账号触发了风控验证,同时验证码资源因会话失效、Cookie 异常、网络拦截或沙箱环境临时故障而加载失败。

先用浏览器开发者工具检查验证码图片请求,再清除支付宝沙箱域名的 Cookie,重新登录沙箱买家账号,并创建一笔新交易。如果更换浏览器、账号和网络后仍然无法加载验证码,基本可以判断是支付宝沙箱侧异常,需要稍后重试或向支付宝开放平台反馈。

可能原因

沙箱账号触发风控

连续登录、频繁创建订单或短时间内重复支付,都可能让沙箱买家账号进入额外验证流程。验证码是否出现由支付宝页面决定,商户应用无法通过接口参数将其关闭。

验证码图片通常依赖当前登录会话。如果页面保留了旧 Cookie、登录状态已经过期,或者验证码请求没有携带正确的会话信息,就可能出现图片空白、加载失败或 HTTP 403。

只更换浏览器不一定能排除这类问题,因为不同浏览器可能仍在使用相同的网络、代理或 DNS 环境。

验证码资源被拦截

广告过滤扩展、隐私保护功能、公司代理、VPN、抓包工具、防火墙或 DNS 过滤,都可能拦截验证码资源。常见表现包括:

  • ERR_BLOCKED_BY_CLIENT
  • 403 Forbidden
  • 502 Bad Gateway
  • DNS 解析失败
  • TLS 证书错误
  • 请求一直处于 Pending 状态

使用了过期支付链接

支付链接通常包含交易和会话信息。反复刷新以前的支付页面,或者隔了很长时间继续使用原跳转地址,可能会进入异常验证状态。

沙箱环境临时异常

沙箱不是生产环境,其稳定性和数据持久性不能完全按照生产环境来预期。如果验证码接口、登录服务或静态资源服务暂时异常,应用侧无法直接修复。

排查步骤

1. 确认请求发送到了沙箱网关

检查生成支付请求时使用的网关配置,不要混用沙箱应用、沙箱账号和生产网关。支付宝沙箱常见网关为:

https://openapi-sandbox.dl.alipaydev.com/gateway.do

网关地址可能随官方调整而变化,应以支付宝开放平台当前的沙箱文档和控制台配置为准。不要根据支付页面中的验证码地址反推接口环境。

同时确认:

  • app_id 是当前沙箱应用的 App ID。
  • 使用的是该沙箱应用对应的密钥。
  • 登录支付页面时使用的是沙箱买家账号。
  • 没有混用生产环境的账号、密钥或网关。
  • 每次测试都使用新的商户订单号。

2. 重新创建交易

不要继续刷新原来的支付页面。关闭支付窗口,生成唯一的 out_trade_no,然后重新调用支付接口。

例如:

String outTradeNo = "sandbox_" + System.currentTimeMillis();

AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setReturnUrl(returnUrl);
request.setNotifyUrl(notifyUrl);

request.setBizContent(
    "{"
        + "\"out_trade_no\":\"" + outTradeNo + "\","
        + "\"total_amount\":\"0.01\","
        + "\"subject\":\"沙箱支付测试\","
        + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\""
        + "}"
);

String form = alipayClient.pageExecute(request).getBody();

这段示例只展示重新创建交易所需的主要逻辑。实际项目应使用 JSON 序列化工具构造 biz_content,以免手工拼接时错误处理特殊字符。

3. 清理登录状态

按以下顺序操作:

  1. 退出沙箱支付页面。
  2. 删除支付宝沙箱相关站点的 Cookie 和站点数据。
  3. 关闭所有相关页面。
  4. 打开无痕窗口,重新登录沙箱买家账号。
  5. 回到商户应用创建新订单并跳转,不要使用浏览器历史记录中的支付地址。

如果控制台提供重置沙箱买家账号登录密码或支付密码的功能,也可以重置后重新测试。具体入口以当前支付宝开放平台控制台为准。

4. 检查验证码图片请求

打开浏览器开发者工具并进入 Network,刷新支付页面,然后筛选 Img,或者搜索 captcha、verify 等关键词。

需要检查:

  • 请求 URL 是否完整。
  • HTTP 状态码是否为 200。
  • 响应的 Content-Type 是否为图片类型。
  • 请求是否携带 Cookie。
  • 是否发生重定向循环。
  • 控制台是否出现 CSP、混合内容或证书错误。
  • 响应内容究竟是图片,还是登录页或错误页。

如果图片请求返回 200,响应内容却是 HTML,通常表示登录会话已经失效。遇到这种情况应重新登录,无需修改支付接口代码。

5. 排除本地网络干扰

暂时关闭以下组件,再重新测试:

  • 广告过滤和隐私保护扩展
  • VPN 或代理软件
  • HTTP/HTTPS 抓包工具
  • 浏览器的严格反跟踪模式
  • 公司网络的安全过滤程序

也可以切换到手机热点测试。如果验证码在手机热点下能够正常显示,但在原网络中无法显示,问题大概率出在代理、DNS、防火墙或证书链上。

从开发者工具中复制实际请求失败的图片 URL,再用以下命令检查连接情况:

curl -v '从开发者工具复制的验证码图片URL'

验证码请求通常依赖 Cookie 和短期会话,因此直接执行 curl 时返回未登录或拒绝访问属于正常现象。这个命令主要用于检查 DNS、TLS 和网络连接,不能替代浏览器中的会话测试。

6. 更换沙箱买家账号

如果只有某个买家账号要求输入验证码,可以换一个沙箱买家账号测试:

  • 新账号正常:原账号可能处于异常登录或风控状态。
  • 所有账号都异常:继续检查网络,或者考虑沙箱服务是否发生故障。
  • 同一账号在其他网络中正常:优先检查当前网络环境。

7. 判断是否为沙箱侧故障

出现以下情况时,应用代码出错的可能性较低:

  • 多个浏览器都无法加载。
  • 清除 Cookie 并重新登录后仍然失败。
  • 更换沙箱买家账号后仍然失败。
  • 更换网络后仍然失败。
  • 开发者工具显示验证码服务器返回 5xx,或者资源请求长时间没有响应。
  • 支付请求已经成功跳转到支付宝托管页面,问题只发生在验证码区域。

此时应保存问题发生时间、沙箱 App ID、接口名称、订单号、失败请求状态码和页面截图,并向支付宝开放平台技术支持反馈。不要提交应用私钥、支付密码、完整 Cookie 或其他敏感信息。

同步回调不是判断支付成功的依据

同步跳转 return_url 只用于改善页面体验。用户关闭页面、验证码加载失败或网络中断时,同步跳转都可能不会发生。业务系统应以异步通知 notify_url 或主动查询到的交易结果为准。

处理异步通知时必须验签,并核对订单号、金额、商户身份和交易状态:

Map<String, String> params = new HashMap<>();

for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
    params.put(entry.getKey(), String.join(",", entry.getValue()));
}

boolean verified = AlipaySignature.rsaCheckV1(
    params,
    alipayPublicKey,
    StandardCharsets.UTF_8.name(),
    "RSA2"
);

if (!verified) {
    response.getWriter().write("failure");
    return;
}

String outTradeNo = params.get("out_trade_no");
String tradeStatus = params.get("trade_status");
String totalAmount = params.get("total_amount");
String appId = params.get("app_id");

if (!expectedAppId.equals(appId)
        || !orderService.amountMatches(outTradeNo, totalAmount)) {
    response.getWriter().write("failure");
    return;
}

if ("TRADE_SUCCESS".equals(tradeStatus)
        || "TRADE_FINISHED".equals(tradeStatus)) {
    orderService.markPaidIdempotently(outTradeNo, params.get("trade_no"));
}

response.getWriter().write("success");

异步通知接口必须能够被支付宝服务器访问。本机的 localhost 或内网地址不能直接用作有效的 notify_url,除非使用了合规、稳定的公网转发方案。

注意事项

  • 不要尝试在商户页面中替换、代理或自行生成支付宝验证码。
  • 不要通过自动刷新或脚本重试等方式绕过验证码,以免进一步触发风控。
  • 不要重复使用同一个 out_trade_no 创建不同的测试订单。
  • 如果验证码故障发生在支付宝托管页面,修改 return_url 或同步回调代码通常没有作用。
  • 调试日志可以记录接口名称、订单号、返回码和时间,但不能记录私钥、支付密码、Cookie 或完整的敏感参数。
  • 沙箱恢复后,仍需测试异步通知、主动查询和幂等处理,不能只确认浏览器是否成功跳回商户页面。

备注:内容仅供参考。