PAYATHON 2026

支付宝扫码打开h5收银台后如何控制支付完成页跳转

支付老李

结论

支付完成页中“完成”按钮的跳转地址,一般需要在创建支付订单时通过同步返回地址参数指定。支付宝手机网站支付通常使用 return_url;如果接入的是间联收单机构的统一下单接口,则需要查阅该机构的接口文档,确认其使用的是 return_url、front_url、redirect_url,还是其他表示“支付完成跳转地址”的参数。

这个跳转能否由商户控制,取决于间联机构是否会将参数透传给支付宝。如果接口没有提供相应参数,或不支持透传,商户前端就无法直接修改支付宝收银台“完成”按钮的行为,需要联系收单机构开通或补充这一能力。

为什么不能只在前端设置

用户进入支付宝收银台后,支付结果页由支付宝客户端或支付宝页面控制,已经脱离商户自己的 H5 页面。因此,在原商户页面中执行下面的代码,无法修改支付宝结果页的“完成”按钮:

window.location.href = 'https://merchant.example.com/pay/result';

这段代码只能控制商户页面本身的跳转。

还要注意区分两个容易混淆的地址:

  • return_url:供用户同步跳转的地址。支付后点击“完成”时,用户可能会返回这个地址。
  • notify_url:支付宝或收单机构向商户服务端发送支付结果通知的地址,与用户是否点击“完成”无关。

支付状态必须以服务端收到的异步通知或主动查询的结果为准,不能仅根据浏览器是否发生跳转来判断支付成功。

解决步骤

1. 检查间联统一下单接口

先在统一下单接口文档中查找以下类型的字段:

return_url
front_url
redirect_url
success_url
callback_url

不同机构对 callback_url 的定义并不一致,有些机构用它表示异步通知地址,因此具体含义必须以收单机构的接口文档为准。

如果接口同时提供前台返回地址和后台通知地址,需要分别设置:

{
  "return_url": "https://merchant.example.com/pay/result",
  "notify_url": "https://api.merchant.example.com/payment/notify"
}

字段名称、所在层级和签名规则都要遵循间联机构的定义,不能直接套用支付宝开放平台的格式。

2. 将跳转地址放入下单请求

假设间联接口使用 return_url 表示支付完成后的前台返回地址,可以按下面的结构组织请求:

{
  "merchant_order_no": "ORDER_202609090001",
  "amount": "10.00",
  "subject": "门店消费",
  "return_url": "https://merchant.example.com/pay/result?order_no=ORDER_202609090001",
  "notify_url": "https://api.merchant.example.com/payment/notify"
}

这只是结构示例,不代表某个收单机构实际使用的参数名称。金额单位、签名字段和 URL 编码要求,都应以对方的接口文档为准。

如果间联机构最终调用支付宝手机网站支付接口 alipay.trade.wap.pay,通常需要由该机构把商户传入的地址映射到支付宝请求的顶层参数 return_url。

3. 在返回页面重新查询订单状态

用户返回指定页面后,不要马上展示“支付成功”。返回参数可能被伪造,用户也可能在支付结果尚未同步时进入该页面。

前端应携带商户订单号,向自己的服务端查询订单状态:

<script>
const params = new URLSearchParams(window.location.search);
const orderNo = params.get('order_no');

async function loadPaymentResult() {
  if (!orderNo) {
    document.body.textContent = '缺少订单号';
    return;
  }

  const response = await fetch(
    `/api/orders/${encodeURIComponent(orderNo)}/payment-status`,
    {
      method: 'GET',
      credentials: 'include'
    }
  );

  if (!response.ok) {
    document.body.textContent = '订单状态查询失败,请稍后重试';
    return;
  }

  const result = await response.json();

  if (result.status === 'PAID') {
    window.location.replace('/pay/success');
  } else if (result.status === 'PENDING') {
    document.body.textContent = '支付结果确认中,请稍后刷新';
  } else {
    document.body.textContent = '订单尚未支付';
  }
}

loadPaymentResult();
</script>

服务端返回的状态应来自已经验证过的异步通知处理结果,或者由服务端调用收单机构的订单查询接口获取。

4. 正确处理异步通知

服务端收到发往 notify_url 的请求后,需要完成以下处理:

  1. 验证签名和通知来源。
  2. 核对商户订单号、收单机构交易号、金额及商户身份。
  3. 确认支付状态是否成功。
  4. 幂等更新订单,防止重复通知造成重复发货或重复记账。
  5. 按接口协议返回确认内容。

示意代码如下:

@PostMapping("/payment/notify")
public String paymentNotify(HttpServletRequest request) {
    Map<String, String> parameters = extractParameters(request);

    if (!paymentService.verifySignature(parameters)) {
        return "fail";
    }

    String orderNo = parameters.get("merchant_order_no");
    String tradeStatus = parameters.get("trade_status");

    if ("SUCCESS".equals(tradeStatus)) {
        paymentService.confirmPaymentIdempotently(orderNo, parameters);
    }

    return "success";
}

代码中的字段名、支付成功状态值和通知响应格式仅作示意,实际实现必须遵循间联机构的协议。

如果接口没有跳转参数

如果统一下单接口不支持设置前台返回地址,可以依次确认:

  1. 询问间联收单机构是否存在未公开或需要单独开通的前台跳转参数。
  2. 确认该机构使用的支付宝产品是否支持 return_url。
  3. 要求机构在向支付宝下单时透传商户提供的返回地址。
  4. 如果对方明确不支持,商户就无法控制支付宝结果页的“完成”按钮。

这种情况下,可以调整商户 H5 自己的支付流程,例如在发起支付前保留订单页,并在用户回到商户页面后轮询支付状态。但这种处理无法保证用户点击支付宝“完成”后一定返回指定页面。

注意事项

  • return_url 只用于用户跳转,不能作为可靠的支付成功凭证。
  • 用户可能不点击“完成”,而是直接关闭页面;网络问题也可能导致跳转失败。
  • 跳转地址建议使用公网可访问的 HTTPS 地址。
  • 不要允许客户端随意传入跳转域名,否则可能产生开放重定向漏洞。服务端应使用固定域名或域名白名单。
  • 不要在 URL 中传递手机号、用户身份凭证、支付密钥等敏感信息。
  • 返回地址包含查询参数时,应按照收单机构的要求进行 URL 编码,防止 &、? 等字符影响请求解析或签名。
  • 支付宝客户端、系统浏览器和内置 WebView 的返回行为可能不同,需要分别测试。
  • 如果码牌可能由多人扫码使用,订单号必须由服务端生成,并正确关联当前交易、门店和金额。

备注:内容仅供参考。