PAYATHON 2026

H5支付完成后为何未直接跳转到returnUrl?

支付小周

结论

这是 H5 支付的正常交互流程。

在 alipay.trade.wap.pay 场景中,支付宝 App 内的“完成”按钮用于结束收银台并返回发起支付的浏览器页面,并不会直接访问商户配置的 return_url。回到浏览器后,用户还需要点击页面上的“已支付完成返回查看订单”,随后才会进入 return_url。

正式环境也应按这一流程设计。页面样式、按钮文案和部分机型的自动跳转表现可能有所不同,业务系统不能依赖“完成”按钮直接跳转到 return_url。

为什么不能直接跳转

H5 支付会在支付宝 App 和手机浏览器两个独立环境中运行,完整流程如下:

  1. 用户在手机浏览器中打开商户页面。
  2. 浏览器通过 URL Scheme 等方式拉起支付宝 App。
  3. 用户在支付宝 App 中完成支付。
  4. 支付宝 App 结束收银台流程,并尝试返回原浏览器。
  5. 浏览器中的支付宝中间页提示用户确认已经完成支付。
  6. 用户点击确认按钮后,中间页跳转到商户的 return_url。

支付宝 App 无法稳定控制用户原先使用的浏览器,也不能保证浏览器允许跨应用自动唤起和重定向。操作系统、浏览器及其安全策略都可能影响返回过程,因此需要中间确认页衔接 App 和浏览器。

还需要区分两个回调地址:

  • return_url:前端同步跳转地址,主要用于展示支付完成后的页面。
  • notify_url:服务端异步通知地址,用于可靠地确认支付结果。

如果用户关闭页面、浏览器拦截跳转、网络异常,或者用户没有点击确认按钮,return_url 就可能不会被访问。因此,不能把它作为判断订单支付成功的唯一依据。

推荐处理方式

1. 正确配置两个回调地址

发起 alipay.trade.wap.pay 请求时,应同时配置 return_url 和 notify_url:

AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();

request.setReturnUrl("https://www.example.com/pay/return");
request.setNotifyUrl("https://www.example.com/pay/notify");

request.setBizContent("""
{
  "out_trade_no": "ORDER_202609080001",
  "total_amount": "99.00",
  "subject": "订单支付",
  "product_code": "QUICK_WAP_WAY"
}
""");

AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
String paymentForm = response.getBody();

这两个地址都应能通过公网访问,正式环境建议使用 HTTPS。

2. 在 notify_url 中更新订单状态

收到异步通知后,先完成验签,再核对订单号、金额、商户身份和交易状态。确认各项信息无误后更新订单状态,并返回纯文本 success。

@PostMapping("/pay/notify")
public String handleNotify(HttpServletRequest request) throws Exception {
    Map<String, String> params = extractAlipayParams(request);

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

    if (!verified) {
        return "failure";
    }

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

    // 还应核对 app_id、seller_id、订单金额等业务数据。
    if ("TRADE_SUCCESS".equals(tradeStatus)
            || "TRADE_FINISHED".equals(tradeStatus)) {
        markOrderPaidIdempotently(outTradeNo, totalAmount);
    }

    return "success";
}

支付宝可能重复发送异步通知,因此订单更新必须支持幂等处理。

3. 在 return_url 页面主动查询订单状态

用户返回商户页面后,不要立即显示“支付成功”。页面应根据商户订单号查询服务端保存的订单状态。如果异步通知还没有到达,可以由服务端调用 alipay.trade.query 补充确认。

async function refreshPaymentResult(outTradeNo) {
  const response = await fetch(
    `/api/orders/${encodeURIComponent(outTradeNo)}/payment-status`,
    {
      credentials: "include",
      cache: "no-store"
    }
  );

  const result = await response.json();

  if (result.status === "PAID") {
    window.location.replace(`/orders/${encodeURIComponent(outTradeNo)}`);
    return;
  }

  document.querySelector("#payment-status").textContent =
    "支付结果确认中,请稍后刷新";
}

可以按以下流程处理:

return_url 页面
    → 查询商户服务端订单状态
    → 已支付:展示成功或进入订单详情
    → 未确认:服务端调用 alipay.trade.query
    → 仍未确认:提示用户稍后刷新

注意事项

  • 不要把用户访问 return_url 视为支付成功的证明。
  • 不要根据 return_url 中未经验证的参数直接修改订单状态。
  • 用户没有返回商户页面不代表支付失败。用户可能已经付款,只是关闭了浏览器或没有点击确认按钮。
  • notify_url 需要完成验签、订单匹配、金额核对和幂等处理。
  • 沙箱与正式环境的页面表现可能不同,不能据此认定按钮文案或自动跳转行为一定相同。
  • 前端可以提供“我已完成支付”按钮,但点击后应查询真实的订单状态,不能直接把订单标记为已支付。
  • 如需更稳定地确认支付结果,应结合异步通知和主动查询完成支付闭环,不要依赖 App 自动跳转回浏览器。

备注:内容仅供参考。