PAYATHON 2026

Android 端通过 URL 跳转支付宝后如何获取支付回调

支付老李

结论

通过 alipays://platformapi/startapp?... 启动支付宝,只能把用户带到支付宝页面。支付宝不会通过 Activity Result、Intent 或应用重新进入前台等方式,向发起跳转的 Android 应用可靠地返回支付结果。

要准确判断支付是否成功,需要使用支付宝开放平台提供的支付产品,并建立完整的服务端处理流程:

  1. 服务端创建订单并完成签名。
  2. Android 端调用支付宝 App 支付 SDK。
  3. 客户端接收 SDK 返回的结果,用于即时更新页面提示。
  4. 服务端接收支付宝异步通知,验签后更新订单。
  5. 必要时,服务端调用订单查询接口确认最终状态。

发货、充值或开通权益时,应以服务端验签后的异步通知或主动查询结果为准。

为什么当前跳转方式拿不到回调

示例代码本质上只是一次普通的外部应用跳转:

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(jumpUrl));
activity.startActivity(intent);

Android 只负责找到能够处理 alipays:// Scheme 的应用并打开该地址。这个跳转过程并未约定支付宝需要:

  • 调用当前应用的某个回调;
  • 返回支付状态;
  • 调用 setResult();
  • 携带商户订单号返回;
  • 在用户付款后自动唤醒原应用。

所以,即使改用 startActivityForResult(),通常也拿不到支付结果。用户返回应用、支付宝页面关闭或应用重新进入前台,都不能说明付款已经成功。

固定二维码链接也可能只是一个收款入口,未必包含商户订单所需的订单号、签名和异步通知地址等信息,因此无法形成可靠的订单状态闭环。

推荐方案:接入支付宝 App 支付

1. 服务端创建订单

订单必须由商户服务端生成。服务端至少需要完成以下工作:

  • 创建唯一的商户订单号 out_trade_no;
  • 设置金额、标题等业务参数;
  • 配置支付宝异步通知地址 notify_url;
  • 使用商户私钥为请求参数签名;
  • 将生成的订单字符串返回给 Android 客户端。

商户私钥不能放在 Android 应用中,否则可能因应用被反编译而泄露,并被用于伪造订单。

2. Android 调用支付 SDK

下面的代码只展示客户端调用结构。orderInfo 必须由服务端生成,客户端不能自行拼接或签名:

ExecutorService executor = Executors.newSingleThreadExecutor();

executor.execute(() -> {
    PayTask payTask = new PayTask(activity);

    // orderInfo 由商户服务端生成并签名
    Map<String, String> result = payTask.payV2(orderInfo, true);

    activity.runOnUiThread(() -> {
        String resultStatus = result.get("resultStatus");
        String resultInfo = result.get("result");

        // 这里只更新页面提示,不在此处直接发货或开通权益
        if ("9000".equals(resultStatus)) {
            showMessage("支付请求已完成,正在确认订单状态");
        } else if ("8000".equals(resultStatus)) {
            showMessage("支付结果确认中");
        } else if ("6001".equals(resultStatus)) {
            showMessage("用户取消支付");
        } else {
            showMessage("支付未完成");
        }

        queryOrderFromMerchantServer();
    });
});

PayTask.payV2() 是阻塞调用,不能放在主线程中执行。SDK 版本和依赖配置应以接入时使用的支付宝开放平台文档为准。

常见的 resultStatus 如下:

状态码常见含义
9000客户端认为支付成功
8000结果确认中
6001用户取消
6002网络连接异常

这些状态码只适合用来更新客户端提示。即使收到 9000,也不能只凭客户端返回的结果发货或充值。

3. 服务端处理异步通知

支付宝向 notify_url 发送通知后,服务端需要:

  1. 读取通知参数;
  2. 按照支付宝的规则验证签名;
  3. 检查 app_id 和商户身份等信息;
  4. 根据 out_trade_no 查找本地订单;
  5. 核对订单金额和币种;
  6. 判断交易状态;
  7. 以幂等方式更新订单;
  8. 按协议返回成功响应。

处理流程可以概括为:

public String handleAlipayNotify(Map<String, String> params) {
    boolean valid = verifyAlipaySignature(params, alipayPublicKey);
    if (!valid) {
        return "failure";
    }

    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");

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

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

    if (!order.getAmount().toPlainString().equals(totalAmount)) {
        return "failure";
    }

    if ("TRADE_SUCCESS".equals(tradeStatus)
            || "TRADE_FINISHED".equals(tradeStatus)) {
        // 必须幂等,防止重复通知导致重复发货
        orderService.markPaidIfNecessary(order);
        return "success";
    }

    return "success";
}

这段代码只用于说明处理流程。验签参数转换、金额比较、通知响应格式和交易状态处理方式,需要按照实际使用的支付宝服务端 SDK 及支付产品要求实现。

4. 客户端查询商户服务端

客户端支付流程结束后,应查询商户自己的服务端,不能直接把 SDK 返回值当作最终支付结果:

private void queryOrderFromMerchantServer() {
    merchantApi.queryOrder(outTradeNo, order -> {
        if ("PAID".equals(order.getStatus())) {
            showPaymentSuccess();
        } else if ("PAYING".equals(order.getStatus())) {
            showPaymentProcessing();
        } else {
            showPaymentPending();
        }
    });
}

如果服务端暂时还没有收到异步通知,可以由服务端调用支付宝订单查询接口补充确认。支付宝私钥、验签公钥和服务端接口凭证都不能交给客户端。

如果必须保留 URL 跳转

用户返回应用后,可以提示”正在查询支付结果”,再通过业务订单号向商户服务端查询。不过,这要求二维码支付流程能够关联商户系统创建的唯一订单,并且服务端可以通过支付宝通知或查询接口获取该订单的状态。

不能通过以下方式判断付款成功:

@Override
protected void onResume() {
    super.onResume();

    // 错误:返回应用不代表支付成功
    markOrderAsPaid();
}

也不能依赖:

startActivityForResult(intent, REQUEST_PAY);

外部 Scheme 跳转没有约定支付宝必须返回有效的 resultCode 和支付数据。

注意事项

  • 不能根据应用重新进入前台、支付宝页面关闭或用户截图判断支付成功。
  • 不能只依赖客户端的 resultStatus。客户端返回值可能丢失、延迟或被篡改。
  • 不要在 Android 端保存商户私钥或完成订单签名。
  • 服务端必须校验订单号、金额、商户身份和签名,不能只检查 trade_status。
  • 异步通知可能重复发送,订单更新和发货逻辑必须具备幂等性。
  • URL 参数需要正确编码,但编码只能解决链接格式问题,不能让普通 Scheme 跳转获得支付回调能力。
  • 如果使用个人收款码、固定收款码或非开放平台商户支付产品,通常无法获得完整且可靠的订单回调。需要改用支持订单创建、异步通知和订单查询的商户支付产品。

备注:内容仅供参考。