PAYATHON 2026

调用 alipay.user.agreement.page.sign 后打开返回接口出现 404

支付阿杰

结论

这类 404 通常不代表 alipay.user.agreement.page.sign 签约失败,更常见的原因是签约页面打开方式错误、网关环境混用,或者跳转地址不可用。

建议先检查三件事:

  1. 是否把 SDK 返回的 HTML 表单当作普通 URL 打开。
  2. 沙箱和生产环境的网关、应用及密钥是否混用。
  3. 404 来自支付宝页面,还是商户自己的 return_url。

先找到实际返回 404 的地址,才能判断问题发生在请求支付宝、打开签约页,还是签约完成后的回跳阶段。

先确认 404 出现在哪个地址

打开浏览器开发者工具,在 Network 面板中找到返回 404 的请求,并记录完整 URL、状态码和响应内容。

通常有以下几种情况:

  • 地址属于 openapi.alipay.com:检查生产网关、请求参数和页面请求方式。
  • 地址属于 openapi-sandbox.dl.alipaydev.com:检查沙箱应用和沙箱账号配置。
  • 地址属于商户自己的域名:通常是 return_url 路由不存在、路径错误或尚未部署。
  • 打开的内容以 <form 开头:返回值是 HTML 表单,不是 URL,不能直接放入地址栏。
  • 地址中包含 %3Cform 之类被编码的 HTML:程序可能错误地对表单内容进行了 URL 编码或拼接。

不能只看页面显示的“404”,还要确认最终访问的是哪个域名和路径。

正确处理页面类接口的返回值

alipay.user.agreement.page.sign 是页面类接口。使用支付宝 SDK 时,一般需要通过页面执行方法生成跳转表单,再把表单原样输出给浏览器。

以下是 Java SDK 示例。具体类名和方法签名以项目实际使用的 SDK 版本为准:

AlipayClient alipayClient = new DefaultAlipayClient(
    "https://openapi.alipay.com/gateway.do",
    appId,
    merchantPrivateKey,
    "json",
    "UTF-8",
    alipayPublicKey,
    "RSA2"
);

AlipayUserAgreementPageSignRequest request =
    new AlipayUserAgreementPageSignRequest();

request.setReturnUrl("https://merchant.example.com/alipay/agreement/return");
request.setNotifyUrl("https://merchant.example.com/alipay/agreement/notify");

// 字段请按照当前接口文档和实际签约产品填写
request.setBizContent("""
{
  "external_agreement_no": "AGREEMENT_202609090001",
  "personal_product_code": "YOUR_PERSONAL_PRODUCT_CODE",
  "sign_scene": "YOUR_SIGN_SCENE"
}
""");

AlipayUserAgreementPageSignResponse response =
    alipayClient.pageExecute(request);

String form = response.getBody();

如果 form 是 HTML,应直接将它作为 HTML 响应返回:

response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(form);
response.getWriter().flush();

不要使用下面的写法:

response.sendRedirect(form);

sendRedirect 接收的是 URL,而 pageExecute() 返回的可能是一段会自动提交的 HTML <form>。将表单作为 URL 重定向,可能导致错误路径、404 或 URI 过长。

如果当前 SDK 提供 URL 生成方法,也要先确认返回值确实以 https:// 开头,再执行重定向。不能只根据方法名称判断返回类型。

检查沙箱与生产环境是否混用

生产环境通常使用:

https://openapi.alipay.com/gateway.do

沙箱环境通常使用:

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

同一笔请求中的这些配置必须来自同一个环境:

  • 网关地址
  • app_id
  • 商户私钥
  • 支付宝公钥或支付宝证书
  • 签约产品配置
  • 测试账号或正式账号

例如,用沙箱 app_id 请求生产网关,或者用生产密钥请求沙箱网关,都是错误配置。这类问题不一定返回 404,也可能表现为签名错误、应用无效或业务参数错误,因此需要同时查看支付宝的原始响应。

检查 return_url 是否存在

如果用户已经进入签约页面,完成操作后才看到 404,问题通常出在商户的 return_url。

可以直接在浏览器中访问这个地址:

https://merchant.example.com/alipay/agreement/return

检查以下内容:

  • 路由是否已部署到当前环境。
  • HTTP 方法是否与后端路由匹配。
  • 网关或反向代理是否改写了路径。
  • 域名、端口和上下文路径是否正确。
  • HTTPS 证书是否有效。
  • 登录拦截器是否把请求跳转到了不存在的页面。
  • 应用发布后,该地址能否从外部网络访问。

支付宝回跳时可能附带查询参数,因此路由不能只匹配某个固定的完整 URL。

例如:

@GetMapping("/alipay/agreement/return")
public String agreementReturn(HttpServletRequest request) {
    // 验签并查询最终签约状态
    return "agreement-result";
}

return_url 只负责浏览器回跳,不能单独作为最终业务结果的可信依据。最终签约状态需要结合异步通知或主动查询确认。

检查 notify_url 与 return_url 是否混淆

这两个地址用途不同:

  • return_url:支付宝处理完成后,将用户浏览器跳回商户页面。
  • notify_url:支付宝服务器向商户服务器发送异步通知。

notify_url 通常不返回网页,也不能依赖浏览器的登录状态。后端收到通知后,应按接口要求验签,并返回规定的成功响应。

如果把只支持 POST 的通知地址配置为 return_url,而浏览器回跳使用 GET,就可能返回 404 或 405。

检查原始请求和响应

建议记录以下信息,但不要记录或输出商户私钥:

gateway
app_id
method
sign_type
charset
timestamp
return_url
notify_url
biz_content
HTTP status
response body
最终跳转 URL

然后判断返回内容属于哪种类型。

<form name="punchout_form" method="post" action="...">

这说明返回的是表单,应将其输出给浏览器。

{
  "alipay_user_agreement_page_sign_response": {
    "code": "...",
    "msg": "..."
  }
}

这说明支付宝返回了业务响应。此时应根据 code、sub_code、msg 和 sub_msg 排查,不能继续把响应当作页面打开。

<html>
  <body>404 Not Found</body>
</html>

遇到这种响应,需要结合请求域名判断 404 来自支付宝网关、代理服务器还是商户应用。

还需要排查的常见问题

手工拼接网关 URL

不要自行拼接 sign、biz_content 和其他系统参数。编码、排序或签名范围出错,都可能导致请求异常。页面请求应优先交给官方 SDK 生成。

网关地址缺少路径

网关地址需要包含完整的 /gateway.do。如果只配置:

https://openapi.alipay.com

程序又错误地拼接路径,就可能访问到不存在的地址。

代理或前端路由改写

如果后端先把表单返回给前端,再交给前端框架处理,HTML 可能被当作普通字符串、JSON 或站内路由。对于页面类接口,可以让浏览器直接提交表单,也可以由后端返回正确的 text/html 响应。

返回内容被二次编码

不要对整个 HTML 表单执行 URL 编码,或者经过 JSON 转义后再直接打开。如果通过 JSON 接口把表单交给前端,前端需要还原并提交表单。由后端直接输出 HTML 页面通常更稳妥。

业务参数与产品权限不匹配

如果签约产品尚未开通,或者 personal_product_code、sign_scene 等参数与实际产品不一致,通常会收到明确的业务错误,但具体表现取决于接口和调用链。排查时应以支付宝返回的 sub_code 和 sub_msg 为准,不能把所有异常都当作 404。

推荐排查顺序

  1. 获取发生 404 时浏览器地址栏中的完整地址。
  2. 判断该地址属于支付宝域名还是商户域名。
  3. 打印 SDK 返回值的前几百个字符,确认它是 URL、HTML 表单还是 JSON。
  4. 如果返回 HTML 表单,使用 text/html 原样输出,不要调用 sendRedirect()。
  5. 核对网关、app_id、密钥和账号是否都属于同一个环境。
  6. 单独访问并验证 return_url 路由。
  7. 查看支付宝业务响应中的 code、sub_code 和 sub_msg。
  8. 对回跳参数和异步通知进行验签,再通过查询接口确认最终签约状态。

多数此类问题都与两种情况有关:程序把表单当作 URL 打开,或者签约完成后跳转到了不存在的 return_url。先确定究竟是哪个地址返回了 404,再按调用阶段逐步检查,比反复修改签约参数更容易找到原因。

备注:内容仅供参考。