支付宝沙箱支付登录后点击下一步无反应
结论
后端能生成支付 URL,说明参数组装和签名流程基本执行成功,但不代表沙箱收银台一定能正常登录。
“点击下一步无反应”和”余额为 0、没有充值按钮”通常是两个不同的问题:
- 登录页无响应时,先确认支付 URL 是否指向沙箱网关、登录账号是否为沙箱买家账号,再查看浏览器控制台和网络请求中的报错。
- 充值和提现通常不在支付收银台操作。沙箱余额需要到支付宝开放平台的沙箱账号管理页面维护。如果控制台中没有充值入口,原因可能是控制台改版、账号权限限制或沙箱能力调整,修改 NestJS 支付代码无法解决。
如果请求参数正确,更换浏览器和网络后仍无法登录,而且点击按钮时没有发出任何请求,问题很可能出在支付宝沙箱页面。请保留请求时间、app_id、支付 URL 中的关键参数和浏览器报错,并提交给支付宝开放平台技术支持。
先核对沙箱环境
支付请求必须发送到沙箱网关:
https://openapi-sandbox.dl.alipaydev.com/gateway.do
不要混用以下正式环境信息:
- 正式应用
app_id - 正式应用密钥
- 正式支付宝买家账号
- 正式网关
https://openapi.alipay.com/gateway.do
沙箱应用、沙箱密钥和沙箱买家账号必须来自同一个开放平台沙箱环境。沙箱商家账号不能代替沙箱买家账号登录收银台。
可以直接检查生成 URL 的域名:
const payUrl = await createPayUrl();
const parsedUrl = new URL(payUrl);
console.log({
origin: parsedUrl.origin,
appId: parsedUrl.searchParams.get('app_id'),
method: parsedUrl.searchParams.get('method'),
signType: parsedUrl.searchParams.get('sign_type'),
});
至少应满足以下条件:
origin = https://openapi-sandbox.dl.alipaydev.com
method = alipay.trade.page.pay
sign_type = RSA2
不要在日志中输出完整 URL。支付 URL 中含有签名和业务参数,不适合公开或长期保存。
Node.js/NestJS 配置示例
以下示例使用常见的 alipay-sdk 调用方式。不同版本的 SDK 在字段命名上可能略有差异,请以项目实际安装版本的类型定义和文档为准。
import AlipaySdk from 'alipay-sdk';
const alipaySdk = new AlipaySdk({
appId: process.env.ALIPAY_APP_ID!,
privateKey: process.env.ALIPAY_PRIVATE_KEY!,
alipayPublicKey: process.env.ALIPAY_PUBLIC_KEY!,
gateway: 'https://openapi-sandbox.dl.alipaydev.com/gateway.do',
signType: 'RSA2',
charset: 'utf-8',
});
生成电脑网站支付 URL:
import { Injectable } from '@nestjs/common';
import { randomUUID } from 'node:crypto';
@Injectable()
export class AlipayService {
async createPagePayUrl(): Promise<string> {
const outTradeNo = randomUUID().replaceAll('-', '');
return alipaySdk.exec(
'alipay.trade.page.pay',
{
notifyUrl: 'https://example.com/payment/alipay/notify',
returnUrl: 'https://example.com/payment/alipay/return',
bizContent: {
out_trade_no: outTradeNo,
product_code: 'FAST_INSTANT_TRADE_PAY',
total_amount: '0.01',
subject: '沙箱支付测试',
},
},
{
method: 'GET',
},
);
}
}
需要重点检查:
total_amount应使用字符串形式,例如'0.01'。- 每次测试都应使用唯一的
out_trade_no。 product_code应为FAST_INSTANT_TRADE_PAY。- 不要填反私钥和支付宝公钥。
- 普通公钥模式不能与证书模式混用。
- 不要对 SDK 返回的完整 URL 再执行
encodeURIComponent()。 - 不要截断生成的 URL 后再复制到浏览器。
- 不要缓存或重复使用支付链接。出现问题时,重新生成一笔订单。
如果使用证书模式,应按当前 SDK 的证书配置方式提供应用公钥证书、支付宝公钥证书和支付宝根证书,不要同时套用普通公钥模式的配置。
排查”下一步”没有反应
1. 确认使用沙箱买家账号
登录收银台时,应输入开放平台沙箱账号页面提供的买家账号和登录密码。
注意区分:
- 登录密码:用于登录沙箱账号。
- 支付密码:用于确认支付。
- 商家账号:用于模拟收款方,不能作为买家完成自己的订单。
- 正式支付宝账号:不能登录沙箱收银台。
建议手动输入账号和密码,并暂时关闭密码管理器和浏览器自动填充。有时输入框看起来已经有内容,但页面脚本并未收到正常的输入事件。
2. 查看浏览器开发者工具
按 F12 打开开发者工具,分别检查 Console 和 Network。
点击”下一步”后,可以根据具体表现排查:
- 完全没有网络请求:通常是页面 JavaScript 报错、按钮未通过前端校验、浏览器扩展拦截,或者沙箱页面本身出现异常。
- 请求被浏览器阻止:检查第三方 Cookie、内容拦截、隐私保护、代理和安全软件。
- 返回
4xx或5xx:记录请求地址、响应内容和发生时间。 - 页面提示签名或参数错误:重新检查
app_id、密钥、网关和签名模式。 - 页面长时间处于等待状态:更换网络并关闭代理后重试。
无痕模式不一定更兼容。有些浏览器会在无痕模式下采用更严格的 Cookie 和跨站跟踪限制。可以分别尝试:
- 使用普通窗口;
- 更换 Chrome、Edge 等浏览器;
- 允许当前站点使用 Cookie;
- 禁用广告拦截和脚本拦截扩展;
- 关闭代理或 VPN;
- 清除支付宝沙箱相关的站点数据,然后重新打开页面;
- 更换网络环境。
3. 检查链接是否被二次处理
一个常见错误是再次编码完整 URL:
// 不推荐:会破坏完整支付 URL 的查询参数结构
return encodeURIComponent(payUrl);
应该直接跳转:
return res.redirect(payUrl);
也可以由接口返回:
return {
payUrl,
};
前端直接赋值:
window.location.href = response.payUrl;
通过 JSON 返回时,JSON 序列化会自行处理字符串转义,无需额外编码整个 URL。
4. 检查系统时间
签名请求中的时间参数对环境时间有要求。如果开发机或服务器的时间偏差较大,可能导致请求过期或校验失败。
检查服务器的时区和当前时间,并确保系统已启用时间同步。NestJS 中的请求时间应交给 SDK 正常处理,不要自行拼接已经过期的 timestamp。
余额为 0 和没有充值按钮
支付收银台用于选择付款方式和确认支付,不是沙箱账户管理页面。因此,收银台中没有”充值”和”提现”按钮,并不代表支付代码配置有误。
进入支付宝开放平台对应应用的沙箱环境,在沙箱账号或买家账号管理入口中查看是否可以调整沙箱余额。控制台界面和入口名称可能会随时间变化。
如果当前沙箱控制台确实没有余额维护入口,请依次确认:
- 当前登录的是创建该沙箱应用的开放平台账号。
- 当前打开的是沙箱环境,而不是正式应用页面。
- 使用的是沙箱买家账号,而不是沙箱商家账号。
- 应用已经开通当前测试所需的支付能力。
- 控制台中是否有余额初始化、充值或重置沙箱数据的入口。
如果以上条件都已满足,后台仍然没有相关入口,需要联系支付宝开放平台支持,确认当前沙箱账户的余额维护方式。不要用真实支付宝向沙箱账号转账,正式环境和沙箱环境互不相通。
建议的最小验证流程
可以按以下顺序测试,尽量排除业务代码的干扰:
- 使用沙箱
app_id、沙箱密钥和沙箱网关。 - 创建一笔金额为
0.01元的新订单。 - 确保
out_trade_no从未使用过。 - 将 SDK 原始返回的 URL 直接粘贴到普通浏览器窗口。
- 使用沙箱买家账号和登录密码。
- 打开开发者工具,观察点击”下一步”后的请求和错误。
- 更换浏览器、网络和沙箱买家账号进行交叉验证。
如果多个浏览器、多个网络和不同沙箱买家账号都遇到相同问题,而且点击按钮时没有发出任何业务请求,继续修改 NestJS 签名代码通常没有意义。此时应整理以下信息并提交给技术支持:
- 问题发生的准确时间;
- 沙箱应用
app_id; - 接口名称
alipay.trade.page.pay; - 浏览器及版本;
- Console 错误截图;
- Network 中失败请求的状态码和响应;
- 脱敏后的请求参数;
- 支付页面截图。
不要提交应用私钥、完整签名、登录密码或支付密码。
备注:内容仅供参考。
