PAYATHON 2026

支付宝扫码打开 H5 获取 openId 并唤起支付是否可行?

支付老李

结论

可以实现。

用户通过支付宝扫码进入 H5 后,可先通过支付宝网页授权获取用户标识,再由服务端创建支付订单,最后从 H5 唤起支付宝收银台完成付款。

需要注意两点:

  • 支付宝的用户标识可能是 user_id,也可能是 open_id。具体返回哪个字段,取决于应用类型、已开通能力和当前开放平台接口规则,不能直接套用微信的 openid 概念。
  • 获取用户标识和发起支付是两个独立流程。支付本身不要求预先获取用户标识。只有需要识别、绑定或记录当前支付宝用户时,才需要先授权。

推荐实现流程

完整流程如下:

支付宝扫码
  → 打开商户 H5
  → 跳转支付宝静默授权
  → 回调 H5 并携带 auth_code
  → 服务端用 auth_code 换取用户标识
  → 服务端创建支付订单
  → H5 跳转或提交表单进入支付宝收银台
  → 支付宝异步通知服务端
  → 服务端验签并更新订单状态

授权换取用户标识、创建订单、签名和异步通知验签都必须由服务端完成。应用私钥不能放在 H5 JavaScript 中。

第一步:申请并配置相关能力

先在支付宝开放平台创建应用,再根据实际场景开通以下能力:

  • 支付宝网页授权;
  • 手机网站支付,即 alipay.trade.wap.pay;
  • 如果要在支付宝客户端内使用 JSAPI 支付,还需开通对应的支付产品和权限。

还需要配置:

  • 授权回调地址;
  • 支付完成后的同步跳转地址;
  • 支付结果异步通知地址;
  • 应用公钥证书或公钥;
  • 支付宝公钥证书或支付宝公钥;
  • H5 域名及平台要求的其他安全配置。

应用能否直接取得 open_id,应以应用后台实际开通的产品和 alipay.system.oauth.token 的返回内容为准。如果接口只返回 user_id,就保存 user_id,不要自行生成或转换成所谓的 openId。

第二步:发起静默授权

如果只需识别用户,不需要昵称、头像等信息,可以使用基础授权范围。常见的授权地址如下:

https://openauth.alipay.com/oauth2/publicAppAuthorize.htm
    ?app_id=YOUR_APP_ID
    &scope=auth_base
    &redirect_uri=ENCODED_CALLBACK_URL
    &state=RANDOM_STATE

授权地址可以由服务端生成。前端确认当前请求尚未完成授权后,也可以直接跳转:

const callbackUrl = encodeURIComponent(
  "https://pay.example.com/alipay/oauth/callback"
);

const state = "由服务端生成的一次性随机值";

location.href =
  "https://openauth.alipay.com/oauth2/publicAppAuthorize.htm" +
  "?app_id=YOUR_APP_ID" +
  "&scope=auth_base" +
  "&redirect_uri=" + callbackUrl +
  "&state=" + encodeURIComponent(state);

授权完成后,支付宝会将用户带回回调地址,并附上 auth_code 和原来的 state:

https://pay.example.com/alipay/oauth/callback
    ?auth_code=AUTH_CODE
    &state=RANDOM_STATE

服务端必须校验 state,以防止伪造回调和请求串联。

第三步:服务端换取用户标识

服务端收到 auth_code 后,调用 alipay.system.oauth.token 获取授权结果。下面是 Java 示例,具体类名可能因支付宝 SDK 版本不同而略有差异:

AlipayClient alipayClient = new DefaultAlipayClient(
    gatewayUrl,
    appId,
    appPrivateKey,
    "json",
    "UTF-8",
    alipayPublicKey,
    "RSA2"
);

AlipaySystemOauthTokenRequest request =
    new AlipaySystemOauthTokenRequest();

request.setGrantType("authorization_code");
request.setCode(authCode);

AlipaySystemOauthTokenResponse response =
    alipayClient.execute(request);

if (!response.isSuccess()) {
    throw new IllegalStateException(
        "支付宝授权失败:" + response.getSubMsg()
    );
}

String userId = response.getUserId();

// 部分应用或新版接口模型可能提供 open_id。
// 是否存在对应 getter,应以所使用 SDK 和实际响应为准。
String openId = null;

不能只根据 SDK 中是否存在某个 getter,判断平台是否会返回对应字段。联调时应记录原始业务响应,并对照开放平台当前接口文档和应用权限进行核对。

授权码通常只能使用一次,而且有效期较短。H5 不应保存授权码并反复使用。服务端取得用户标识后,可以建立自己的登录会话,例如通过安全的 HttpOnly Cookie 保存会话编号。

第四步:创建手机网站支付订单

取得用户标识后,服务端调用 alipay.trade.wap.pay 创建手机网站支付订单。该接口通常不要求传入买家的 open_id 或 user_id,买家会在支付宝收银台完成身份确认。

Java 示例:

AlipayTradeWapPayRequest request =
    new AlipayTradeWapPayRequest();

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

AlipayTradeWapPayModel model =
    new AlipayTradeWapPayModel();

model.setOutTradeNo("ORDER_202609090001");
model.setTotalAmount("99.00");
model.setSubject("订单 ORDER_202609090001");
model.setProductCode("QUICK_WAP_WAY");

request.setBizModel(model);

AlipayTradeWapPayResponse response =
    alipayClient.pageExecute(request);

if (!response.isSuccess()) {
    throw new IllegalStateException(
        "创建支付宝支付失败:" + response.getSubMsg()
    );
}

String paymentForm = response.getBody();

paymentForm 通常是一段需要提交至支付宝网关的 HTML 表单。服务端可以直接将其作为 HTML 返回给浏览器:

response.setContentType("text/html;charset=UTF-8");
response.getWriter().write(paymentForm);

浏览器加载表单后会进入支付宝收银台。在支付宝客户端的内置浏览器中,通常会直接进入客户端支付流程。如果页面在其他浏览器中打开,实际体验取决于手机系统、支付宝的安装情况以及平台当时的跳转策略。

另一种方式:支付宝客户端内使用 JSAPI

如果业务明确要求在支付宝客户端内直接调起支付,服务端也可以先通过 alipay.trade.create 创建交易,取得支付宝交易号 trade_no,再由 H5 调用支付宝桥接能力:

function pay(tradeNo) {
  AlipayJSBridge.call(
    "tradePay",
    {
      tradeNO: tradeNo
    },
    function (result) {
      console.log("tradePay result:", result);
    }
  );
}

if (window.AlipayJSBridge) {
  pay("服务端返回的 trade_no");
} else {
  document.addEventListener(
    "AlipayJSBridgeReady",
    function () {
      pay("服务端返回的 trade_no");
    },
    false
  );
}

这种方式对运行环境的要求更严格:

  • 页面必须运行在支持 AlipayJSBridge 的支付宝客户端环境中;
  • 应用必须拥有对应的 JSAPI 和支付权限;
  • 交易仍然必须由服务端创建;
  • JSAPI 回调不能作为最终到账依据。

如果不能保证页面始终在支付宝客户端内打开,建议优先使用 alipay.trade.wap.pay,其兼容性通常更容易控制。

支付结果必须以后端异步通知为准

同步跳转地址 return_url 只用于展示支付结果页面。用户可能中途关闭页面,跳转参数也可能被伪造,因此订单是否支付成功,必须以 notify_url 收到的支付宝异步通知为准。

服务端处理通知时,至少需要完成以下检查和操作:

  1. 使用支付宝公钥验签;
  2. 检查 app_id 是否属于当前应用;
  3. 检查 out_trade_no 是否存在;
  4. 检查通知金额是否与本地订单金额一致;
  5. 检查商户身份相关字段;
  6. 判断交易状态是否符合业务要求;
  7. 幂等更新订单,防止重复发货;
  8. 处理成功后,按支付宝要求返回成功响应。

示意代码:

Map<String, String> params = extractRequestParameters(request);

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

if (!verified) {
    response.getWriter().write("failure");
    return;
}

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

// 继续校验 app_id、商户身份、订单金额和订单状态。
// 更新操作必须保证幂等。

if ("TRADE_SUCCESS".equals(tradeStatus)
        || "TRADE_FINISHED".equals(tradeStatus)) {
    markOrderPaidIdempotently(outTradeNo, totalAmount);
    response.getWriter().write("success");
} else {
    response.getWriter().write("failure");
}

注意事项

  • 扫码打开支付宝 H5,不代表系统已经取得用户身份。二维码中的普通 H5 地址仍需经过网页授权流程。
  • 如果二维码可能被微信、系统相机或普通浏览器扫描,需要增加环境判断和相应提示,不能默认一定存在 AlipayJSBridge。
  • auth_code 应由服务端兑换。应用私钥、支付宝密钥和签名逻辑不得下发到前端。
  • 商品名称、金额和商户订单号必须由服务端根据本地订单生成,不能直接采用前端传入的金额。
  • 每次支付都应使用唯一的 out_trade_no。同一业务订单需要重复支付时,应先查询原交易状态,避免产生多笔有效交易。
  • 用户标识应以支付宝接口实际返回的字段为准。如果返回的是 user_id,不要将其称为 open_id。
  • 获取用户标识并非支付的必要前置条件。如果只需让用户付款,直接创建 WAP 支付订单即可。如果还需进行会员绑定、付款人识别或后续业务关联,再加入静默授权流程。
  • 网页授权、WAP 支付和 JSAPI 支付的准入条件可能随应用类型和平台规则调整。上线前,应在对应应用后台确认当前可用能力,并完成沙箱或正式环境联调。

备注:内容仅供参考。