PAYATHON 2026

H5支付调起支付宝后未跳转到配置的回调页面

支付小周

结论

支付完成后页面直接关闭,通常不代表支付失败。先确认配置的是哪种回调:

  • notify_url 是支付宝服务器发给商户服务器的异步通知,不会让浏览器跳转。
  • return_url 是支付完成后的前端同步跳转地址。
  • 即使设置了 return_url,用户关闭支付宝、WebView 生命周期变化、App Scheme 拉起方式或网络异常,都可能导致同步跳转没有执行。因此,订单状态必须以服务端异步通知或主动查询结果为准,不能依赖回跳页面判断支付是否成功。

先确认实际调用的支付产品

H5 页面支付应使用手机网站支付接口:

alipay.trade.wap.pay

业务参数通常需要包含:

{
  "product_code": "QUICK_WAP_WAY"
}

如果实际调用的是 alipay.trade.app.pay,支付宝会按 App 支付流程处理,不能期待它通过 H5 支付的 return_url 返回网页。

确认请求中确实包含 return_url

不能只看后台或配置文件中的地址,还要检查最终提交给支付宝的请求是否包含 return_url。

Java SDK 示例:

AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();

request.setReturnUrl("https://m.example.com/pay/result");
request.setNotifyUrl("https://api.example.com/alipay/notify");

AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
model.setOutTradeNo("ORDER_202609090001");
model.setTotalAmount("0.01");
model.setSubject("订单支付");
model.setProductCode("QUICK_WAP_WAY");

request.setBizModel(model);

AlipayTradeWapPayResponse payResponse = alipayClient.pageExecute(request);

response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(payResponse.getBody());

不同版本的支付宝 Java SDK 在模型类或方法签名上可能略有差异,请以项目当前使用的 SDK 为准。需要注意以下几点:

  • 在支付请求对象上设置 return_url。
  • 将 notify_url 设置为服务端可以访问的通知接口。
  • 把 pageExecute() 返回的完整 HTML 表单输出给浏览器。
  • 签名完成后不要手工修改或拼接 return_url,否则可能导致验签失败,或者参数无法生效。

如果通过网关参数自行组装请求,重点检查最终请求是否包含以下字段:

method=alipay.trade.wap.pay
return_url=https://m.example.com/pay/result
notify_url=https://api.example.com/alipay/notify

排查回调地址

return_url 最好满足以下条件:

  • 使用完整的绝对地址,例如 https://m.example.com/pay/result。
  • 可以从公网直接访问。
  • 页面打开不依赖当前浏览器的 Session。
  • 不会立即被登录拦截器、网关或前端路由跳转到其他页面。
  • 查询参数已正确编码。
  • HTTPS 证书完整且有效。
  • 页面不受只能在微信、企业 WebView 或特定 App 容器中运行的限制。

可以分别在普通浏览器和支付宝内置浏览器中直接访问该地址,确认页面不会报错、白屏或自动关闭。

如果地址中需要携带订单号,不要直接拼接未经编码的参数:

String returnUrl =
    "https://m.example.com/pay/result?out_trade_no="
    + URLEncoder.encode(outTradeNo, StandardCharsets.UTF_8);
request.setReturnUrl(returnUrl);

检查 H5 拉起支付宝的方式

如果业务页面运行在自有 App 的 WebView 中,通常会通过 URL Scheme 拉起支付宝。支付完成后,支付宝可能返回原应用、关闭中间页,也可能由宿主 App 接管页面生命周期。

这种情况下需要同时检查:

  • WebView 是否拦截了支付宝相关 URL。
  • 拉起支付宝后,原支付页面是否已被销毁。
  • App 恢复到前台时是否主动关闭了 WebView。
  • 是否通过临时页面打开支付表单,并在离开页面时自动将其关闭。
  • iOS 或 Android 是否只配置了 App Scheme 返回逻辑,没有保留 H5 浏览器的回跳链路。

如果业务要求稳定返回自有 App,应在 App 中设计明确的返回链路,并在 App 恢复到前台后向服务端查询订单状态。网页的 return_url 不能代替 App 回调机制。

正确处理支付结果

同步跳转只适合展示结果页,不能作为订单入账的依据。推荐采用以下流程:

用户支付
   ↓
支付宝异步通知 notify_url
   ↓
商户服务端验签并更新订单
   ↓
return_url 结果页查询商户订单状态
   ↓
未确认时轮询或提示“支付结果确认中”

结果页不能只根据 URL 参数显示“支付成功”,而应查询自己的订单接口:

async function waitForPayment(outTradeNo) {
  for (let i = 0; i < 10; i++) {
    const response = await fetch(
      `/api/orders/${encodeURIComponent(outTradeNo)}/status`,
      { credentials: "include" }
    );

    const result = await response.json();

    if (result.status === "PAID") {
      window.location.replace("/pay/success");
      return;
    }

    await new Promise(resolve => setTimeout(resolve, 2000));
  }

  window.location.replace("/pay/pending");
}

处理异步通知

notify_url 接口需要完成以下操作:

  1. 使用支付宝公钥验签。
  2. 校验 app_id、商户身份、订单号和金额。
  3. 根据 trade_status 幂等更新订单。
  4. 处理成功后返回纯文本 success。
  5. 不要返回 JSON、HTML 或包含额外空格的内容。

示意代码:

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

request.getParameterMap().forEach((key, values) -> {
    if (values != null && values.length > 0) {
        params.put(key, String.join(",", values));
    }
});

boolean valid = AlipaySignature.rsaCheckV1(
    params,
    alipayPublicKey,
    "UTF-8",
    "RSA2"
);

if (!valid) {
    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");

boolean orderValid = orderService.checkOrder(
    outTradeNo,
    totalAmount,
    appId
);

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

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

response.setContentType("text/plain;charset=UTF-8");
response.getWriter().write("success");

建议按以下顺序排查

  1. 确认调用的接口是 alipay.trade.wap.pay。
  2. 确认最终支付请求中确实包含 return_url。
  3. 分清 return_url 和 notify_url,不要把异步通知地址当成前端跳转地址。
  4. 确认 return_url 可以在支付宝内置浏览器中正常访问。
  5. 检查是否通过自有 App、WebView 或 Scheme 拉起支付宝,以及原页面是否被宿主关闭。
  6. 查看服务端是否收到发送到 notify_url 的通知。
  7. 如果没有收到通知,检查公网访问、HTTPS、验签日志和接口响应内容。
  8. 对支付状态不确定的订单,通过 alipay.trade.query 主动查询并进行补偿。

“页面回跳”和“支付确认”需要分开处理。return_url 用于完成前端跳转和结果展示,notify_url 与订单查询用于保证资金状态一致。

备注:内容仅供参考。