PAYATHON 2026

支付宝扫码支付成功后如何返回指定链接地址?

支付小周

结论

https://qr.alipay.com/... 这类支付宝收款二维码地址,在支付成功后通常不能像网页支付一样,通过 return_url 强制跳转到商户指定的页面。

扫码支付一般通过异步通知 notify_url 确认交易结果。用户扫码后,支付流程和结果页都由支付宝客户端处理,商户无法控制支付宝结果页的跳转。

如果支付成功后必须打开指定地址,商户需要提供一个中间页面。该页面负责展示二维码并轮询订单状态。后端收到支付宝的异步通知后更新订单,前端检测到支付成功,再跳转到指定页面。

为什么 return_url 不适用于二维码地址

return_url 是同步返回机制,主要用于浏览器网页支付,或者其他支持同步回跳的支付产品。支付完成后,支付宝会将用户的浏览器带回商户页面。

扫码支付的流程则是:

  1. 商户创建订单并取得二维码内容。
  2. 用户使用支付宝扫码或打开 qr.alipay.com 地址。
  3. 用户在支付宝客户端内完成支付。
  4. 支付宝通过 notify_url 通知商户服务器。
  5. 支付宝客户端显示支付结果页。

直接把二维码链接发给用户,页面控制权也就交给了支付宝客户端。即使商户已经得知支付成功,也不能通过自己的 JavaScript 控制支付宝结果页跳转。

推荐方案:中间页加订单状态轮询

不要直接将 qr.alipay.com 地址作为业务入口。可以先生成自己的支付页面,例如:

https://pay.example.com/order/ORDER_202609090001

这个页面展示支付宝二维码,并定期向商户服务器查询订单状态。

完整流程如下:

创建商户订单
    ↓
调用扫码支付接口生成二维码
    ↓
在商户页面展示二维码
    ↓
用户使用支付宝付款
    ↓
支付宝向 notify_url 发送异步通知
    ↓
商户验签并更新订单状态
    ↓
支付页面轮询到成功状态
    ↓
跳转到指定业务地址

前端轮询示例

<img
  src="/api/orders/ORDER_202609090001/qrcode"
  alt="支付宝支付二维码"
>

<script>
const orderNo = "ORDER_202609090001";

const timer = setInterval(async () => {
  try {
    const response = await fetch(
      `/api/orders/${encodeURIComponent(orderNo)}/status`,
      { cache: "no-store" }
    );

    if (!response.ok) {
      return;
    }

    const result = await response.json();

    if (result.status === "PAID") {
      clearInterval(timer);

      // 建议由服务端返回经过校验的站内地址
      window.location.replace(result.redirectUrl);
    }
  } catch (error) {
    console.error("查询支付状态失败", error);
  }
}, 2000);
</script>

订单状态接口可以返回:

{
  "status": "PAID",
  "redirectUrl": "https://www.example.com/payment/success?orderNo=ORDER_202609090001"
}

异步通知处理示例

下面是与编程语言无关的伪代码。实际验签方式应以所使用的支付宝服务端 SDK 为准:

POST /alipay/notify

1. 读取支付宝提交的全部通知参数
2. 使用支付宝公钥验证 sign
3. 检查 app_id 是否属于当前应用
4. 根据 out_trade_no 查询本地订单
5. 校验 seller_id、total_amount 等关键字段
6. 确认 trade_status 为 TRADE_SUCCESS 或业务接受的状态
7. 以幂等方式把订单更新为已支付
8. 向支付宝返回纯文本 success

示意代码:

app.post("/alipay/notify", async (req, res) => {
  const params = req.body;

  const signatureValid = verifyAlipaySignature(params);

  if (!signatureValid) {
    return res.status(400).send("failure");
  }

  const order = await findOrder(params.out_trade_no);

  if (
    !order ||
    params.app_id !== ALIPAY_APP_ID ||
    params.seller_id !== ALIPAY_SELLER_ID ||
    params.total_amount !== order.expectedAmount
  ) {
    return res.status(400).send("failure");
  }

  if (
    params.trade_status === "TRADE_SUCCESS" ||
    params.trade_status === "TRADE_FINISHED"
  ) {
    await markOrderPaidOnce({
      orderNo: params.out_trade_no,
      tradeNo: params.trade_no
    });
  }

  res.type("text/plain").send("success");
});

verifyAlipaySignature、findOrder 和 markOrderPaidOnce 都是示意函数,不能直接用于生产环境。验签时不要自行拼接参数,应使用对应语言的支付宝官方 SDK,或经过验证的签名实现。

如果用户是在同一部手机上打开二维码链接

用户在同一部手机上直接打开 qr.alipay.com 地址后,原商户页面可能会被支付宝客户端覆盖,或者切换到后台。即使商户页面仍在轮询,也无法保证支付宝会在付款后自动切回该页面。

可以通过以下方式改善体验:

  • 支付前提醒用户,付款完成后返回原浏览器页面。
  • 在商户页面提供“我已完成支付”按钮。用户点击后,由后端查询订单状态。
  • 支付成功后发送站内消息、短信或订单通知,引导用户进入结果页。
  • 如果业务必须自动回跳,可改用明确支持同步返回地址的网页支付产品,并根据实际签约产品确认其是否支持 return_url。

注意事项

  • notify_url 用于服务器接收支付结果,不会触发用户页面自动跳转。
  • 不要只根据前端跳转、URL 参数或客户端提示判断付款成功。
  • 支付结果必须通过异步通知验签,必要时还要调用交易查询接口确认。
  • 支付宝可能重复发送异步通知,因此订单更新必须具备幂等性。
  • 必须核对 app_id、seller_id、out_trade_no、total_amount 和交易状态,以免将其他订单误判为已支付。
  • 跳转地址最好由服务器生成,并限制在可信域名内,防止产生开放重定向漏洞。
  • 已经生成的固定 qr.alipay.com 链接,通常不能通过追加普通查询参数获得支付成功后的回跳能力。是否支持其他跳转方式,取决于具体签约的支付宝产品和接口,而不是二维码 URL 本身。

备注:内容仅供参考。