PAYATHON 2026

H5 支付宝花呗分期支付成功后 return_url 未调用

支付小周

结论

不要依赖 return_url 更新订单状态。

return_url 是浏览器端的同步跳转地址。用户关闭或刷新页面、网络中断,或者跳转被拦截,都可能导致它无法访问。判断订单是否支付成功,应以支付宝服务器发送到 notify_url 的异步通知为主,并使用 alipay.trade.query 主动查询来补偿异常情况。

建议采用以下处理方式:

  1. 创建支付时配置 return_url 和 notify_url。
  2. 在 notify_url 中完成验签和订单信息核对,再幂等更新订单。
  3. return_url 只展示支付结果,不确认支付状态。
  4. 前端结果页通过后端接口轮询订单状态。
  5. 定时查询长时间停留在”支付中”状态的订单,调用 alipay.trade.query 进行对账补偿。

为什么 return_url 可能不调用

以 alipay.trade.wap.pay 为例,支付完成后有两类通知:

  • return_url:支付宝通过用户浏览器跳转到商户页面,属于同步跳转。
  • notify_url:支付宝服务器直接请求商户服务器,属于异步通知。

return_url 依赖浏览器继续运行。如果用户支付后立即关闭或刷新页面、切换网络,或者浏览器中断了跳转,请求可能根本无法到达商户服务器,所以不能将它作为确认支付成功的可靠依据。

notify_url 不依赖用户页面,更适合用于更新订单状态。不过,除了正确配置异步通知,还需要准备主动查询机制,以处理网络异常、商户服务不可用等情况。

创建支付时配置异步通知

下面是 Java SDK 的示意代码。具体类名和调用方式以项目实际使用的支付宝 SDK 版本为准:

AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();

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

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

// 花呗分期参数应根据实际签约能力和支付宝接口要求设置
model.setExtendParams(extendParams);

request.setBizModel(model);

String form = alipayClient.pageExecute(request).getBody();

notify_url 必须是支付宝服务器能够访问的公网地址,不能使用 localhost、内网 IP,也不能指向需要登录后才能访问的地址。

正确处理 notify_url

异步通知应按以下流程处理:

  1. 读取支付宝提交的全部参数。
  2. 使用支付宝 SDK 验证签名。
  3. 核对 app_id、商户订单号、金额和收款方等信息。
  4. 确认 trade_status 是可以接受的支付成功状态。
  5. 使用数据库事务幂等更新订单。
  6. 处理成功后返回纯文本 success。

Spring Boot 示例:

@PostMapping(
    value = "/pay/alipay/notify",
    produces = "text/plain;charset=UTF-8"
)
public String alipayNotify(HttpServletRequest request) throws Exception {
    Map<String, String> params = new HashMap<>();

    request.getParameterMap().forEach((key, values) -> {
        params.put(key, String.join(",", values));
    });

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

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

    String appId = params.get("app_id");
    String outTradeNo = params.get("out_trade_no");
    String tradeNo = params.get("trade_no");
    String tradeStatus = params.get("trade_status");
    String totalAmount = params.get("total_amount");

    if (!expectedAppId.equals(appId)) {
        return "failure";
    }

    Order order = orderService.findByOrderNo(outTradeNo);
    if (order == null) {
        return "failure";
    }

    if (new BigDecimal(totalAmount).compareTo(order.getPayAmount()) != 0) {
        return "failure";
    }

    if ("TRADE_SUCCESS".equals(tradeStatus)
            || "TRADE_FINISHED".equals(tradeStatus)) {
        orderService.markPaidIfUnpaid(outTradeNo, tradeNo);
    }

    return "success";
}

生产环境还要根据实际业务核对收款账号、商户身份等字段。不能只看到 trade_status 就直接修改订单状态。

保证订单更新幂等

支付宝可能重复发送异步通知。通知接口必须能够多次处理同一笔交易,同时避免重复发货、充值或记账。

可以在数据库层增加约束,并采用条件更新:

UPDATE orders
SET
    status = 'PAID',
    alipay_trade_no = :tradeNo,
    paid_at = CURRENT_TIMESTAMP
WHERE order_no = :orderNo
  AND status = 'PAYING';

如果影响行数为 0,还要检查订单是否已经处于 PAID 状态。对于已支付的订单,可以直接按成功处理,但不能再次扣减库存或发放权益。

发货、充值等操作可以使用独立业务流水号或唯一索引,进一步避免重复执行。

前端不要直接展示”支付成功”

用户进入 return_url 后,页面应向商户后端查询订单状态,不能根据 URL 参数直接判定支付成功。

async function waitForPayment(orderNo) {
  for (let i = 0; i < 10; i++) {
    const response = await fetch(
      `/api/orders/${encodeURIComponent(orderNo)}/status`,
      { cache: "no-store" }
    );

    const result = await response.json();

    if (result.status === "PAID") {
      showPaymentSuccess();
      return;
    }

    if (result.status === "CLOSED") {
      showPaymentFailed();
      return;
    }

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

  showPaymentProcessing();
}

即使 return_url 没有打开,用户下次进入订单详情页时,仍能从服务器获取真实的订单状态。

增加主动查询补偿

只配置 notify_url 也无法覆盖所有异常。例如,异步通知到达时商户服务不可用,或者通知接口持续返回非 success 内容,都可能导致订单状态更新延迟。

可以定时扫描创建时间超过一定阈值、状态仍为 PAYING 的订单,然后调用 alipay.trade.query:

AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();

AlipayTradeQueryModel model = new AlipayTradeQueryModel();
model.setOutTradeNo(orderNo);
request.setBizModel(model);

AlipayTradeQueryResponse response = alipayClient.execute(request);

if (response.isSuccess()
        && ("TRADE_SUCCESS".equals(response.getTradeStatus())
        || "TRADE_FINISHED".equals(response.getTradeStatus()))) {
    orderService.markPaidIfUnpaid(
        orderNo,
        response.getTradeNo()
    );
}

查询成功后,仍要核对订单号、支付金额和商户身份,并复用异步通知的幂等更新逻辑。

补偿任务的执行频率和订单超时时间,应结合业务量、支付宝接口限制和项目容错要求确定。不要对每个订单进行高频、无限次查询。

注意事项

  • 不要在 return_url 中执行唯一一次的支付确认、发货或充值。
  • 不要相信前端提交的”支付成功”标志,也不要只根据跳转参数修改订单状态。
  • notify_url 不应要求登录,也不应执行页面跳转或输出 HTML。处理成功后直接返回 success。
  • 验签前不要自行拼接或修改支付宝传入的参数。
  • 异步通知接口应记录必要的请求、验签和处理日志,同时避免泄露私钥和完整的敏感数据。
  • 如果通知处理包含耗时操作,应先可靠落库并返回 success,再通过事务消息或任务队列处理后续业务。
  • 浏览器的 beforeunload、unload 或前端 AJAX 无法解决这个问题,因为它们也可能随页面关闭而中断。

完整的支付处理流程应以 notify_url 异步通知为主,以主动查询作为补偿。return_url 只用于展示结果,所有订单更新都必须保持幂等。

备注:内容仅供参考。