PAYATHON 2026

小程序未登录或未授权时如何拉起支付宝支付

支付小周

结论

小程序自身的登录或用户信息授权,并不是拉起支付宝支付的必要条件。

即使用户没有登录商家账号,也没有授权昵称、头像、手机号等资料,小程序仍然可以:

  1. 创建匿名订单或游客订单;
  2. 由服务端调用支付宝交易创建接口;
  3. 将支付宝交易号返回给小程序;
  4. 通过 my.tradePay 拉起支付宝收银台。

喜茶这类小程序通常只是没有要求用户先登录会员账号或授权用户资料,并不意味着支付脱离了支付宝账户体系。支付宝客户端已经掌握当前付款账户,可以在收银台完成身份确认、密码验证或生物识别。

为什么未授权也能支付

这里需要分清三种容易混淆的状态:

  • 商家账号未登录:用户没有登录小程序自己的会员体系。
  • 用户资料未授权:用户没有同意提供手机号、头像、昵称等信息。
  • 支付宝付款账户未登录:支付宝客户端中没有可用的付款账户。

前两种状态通常不会影响支付。遇到第三种情况,支付宝客户端会自行处理,例如提示用户登录支付宝账户。

支付围绕交易订单进行,并不要求用户先有商家会员身份。商家可以为游客创建临时会话和订单,等支付成功后,再根据业务需要引导用户登录、绑定手机号,或把订单归入会员账户。

部分支付接入方案还需要获取支付宝用户标识。这时可以根据支付宝当前提供的开放能力使用基础身份授权,例如通过 my.getAuthCode 获取授权码,再由服务端换取用户标识。基础身份授权和获取手机号、昵称等用户资料是两回事。是否出现授权提示、使用哪个 scope,需要以当前小程序类目、开放平台配置和支付宝官方文档为准。

推荐实现流程

1. 建立游客会话

小程序启动后,由业务服务端生成临时会话标识。这个标识只用于关联购物车、订单和支付结果,不要求用户先注册会员。

const { data } = await my.request({
  url: 'https://api.example.com/guest/session',
  method: 'POST'
});

const guestToken = data.guestToken;

不要直接用设备信息充当用户身份,也不要依赖前端生成的随机值进行订单鉴权。游客会话应由服务端签发,并设置合理的有效期。

2. 在服务端创建业务订单

用户提交订单时,小程序只上传商品、数量、优惠券等必要信息。商品价格、优惠金额、运费和最终支付金额都必须由服务端重新计算。

const { data } = await my.request({
  url: 'https://api.example.com/orders',
  method: 'POST',
  headers: {
    Authorization: `Guest ${guestToken}`
  },
  data: {
    items: [
      {
        skuId: 'SKU_10001',
        quantity: 1
      }
    ]
  }
});

服务端需要生成唯一的业务订单号,例如 out_trade_no,并保存订单状态:

{
  "outTradeNo": "ORDER_20260913_000001",
  "status": "PENDING_PAYMENT",
  "totalAmount": "19.90"
}

3. 如支付接口要求,获取支付宝用户标识

是否需要这一步,取决于所使用的支付宝交易接口及其当前参数要求。

小程序端可以调用 my.getAuthCode 获取短期授权码:

my.getAuthCode({
  scopes: ['auth_base'],
  success: async (res) => {
    await my.request({
      url: 'https://api.example.com/alipay/auth-code',
      method: 'POST',
      data: {
        authCode: res.authCode
      }
    });
  },
  fail: (err) => {
    console.error('获取授权码失败', err);
  }
});

授权码必须发送给业务服务端,再由服务端通过支付宝开放平台接口换取所需的用户标识。支付宝应用私钥不能保存在小程序前端,签名也不能由前端直接完成。

auth_base、返回字段和用户标识字段可能因开放平台接口版本及应用配置而异,接入时应以当前官方文档为准。

4. 服务端创建支付宝交易

服务端使用支付宝开放平台分配的应用身份和私钥调用交易创建接口。常见做法是调用 alipay.trade.create,取得支付宝交易号 trade_no。

下面只展示处理结构,省略具体的 SDK 初始化和参数签名:

app.post('/orders/:orderNo/pay', async (req, res) => {
  const order = await orderService.findPayableOrder(
    req.params.orderNo,
    req.guestSession
  );

  if (!order) {
    return res.status(404).json({ message: '订单不存在' });
  }

  if (order.status !== 'PENDING_PAYMENT') {
    return res.status(409).json({ message: '订单状态不允许支付' });
  }

  const result = await alipayClient.exec('alipay.trade.create', {
    bizContent: {
      out_trade_no: order.outTradeNo,
      total_amount: order.totalAmount,
      subject: order.subject

      // 如果当前接口要求付款方标识,应在这里传入服务端取得的
      // buyer_id 或 buyer_open_id,具体字段以接口文档为准。
    }
  });

  res.json({
    tradeNO: result.trade_no
  });
});

需要注意以下几点:

  • out_trade_no 必须唯一;
  • total_amount 必须取自服务端订单;
  • 支付宝应用私钥只能保存在服务端;
  • 创建交易前必须检查订单归属和状态;
  • 遇到重复请求时,应返回同一笔有效交易,或者按照业务规则关闭旧交易后再创建。

5. 小程序拉起支付宝收银台

小程序拿到服务端返回的 tradeNO 后,调用 my.tradePay:

async function pay(orderNo) {
  const { data } = await my.request({
    url: `https://api.example.com/orders/${orderNo}/pay`,
    method: 'POST',
    headers: {
      Authorization: `Guest ${guestToken}`
    }
  });

  my.tradePay({
    tradeNO: data.tradeNO,
    success: () => {
      queryOrderResult(orderNo);
    },
    fail: (err) => {
      console.error('支付未完成', err);
      queryOrderResult(orderNo);
    }
  });
}

不同版本或支付产品支持的入参形式可能不同。有些场景会使用服务端生成的订单字符串。接入时应根据当前 my.tradePay 文档选择参数,不要混用不同支付产品的示例。

6. 以服务端通知确认支付结果

my.tradePay 的前端回调只能说明收银台的调用结果,不能单独作为发货、出餐或充值的依据。

服务端收到支付宝异步通知后,应完成以下处理:

  1. 验证通知签名;
  2. 校验应用 ID、商户信息和订单号;
  3. 校验支付金额与币种;
  4. 判断交易状态;
  5. 幂等更新业务订单;
  6. 返回支付宝要求的响应内容。

前端结束支付流程后,还需要查询业务服务端保存的订单状态:

async function queryOrderResult(orderNo) {
  const { data } = await my.request({
    url: `https://api.example.com/orders/${orderNo}`,
    method: 'GET',
    headers: {
      Authorization: `Guest ${guestToken}`
    }
  });

  if (data.status === 'PAID') {
    my.showToast({
      type: 'success',
      content: '支付成功'
    });
  } else {
    my.showToast({
      content: '支付结果确认中'
    });
  }
}

游客支付后的订单如何找回

开放未登录支付后,还要考虑用户再次进入小程序时如何查看订单。常见做法包括:

  • 在本地保存游客会话凭证,由服务端维护会话有效期;
  • 支付完成后引导用户绑定手机号或登录会员账号;
  • 用户登录后,经过校验将游客订单合并到会员账号;
  • 提供订单号加手机号等带有安全校验的订单找回方式。

订单合并必须由服务端完成身份校验,不能仅凭前端提供的订单号直接修改订单归属。

注意事项

  • 不要为了支付强制获取头像、昵称或手机号,除非这些信息确实是履约所必需的。
  • 不要信任前端传入的金额、商品名称、优惠金额或订单状态。
  • 不要把支付宝私钥、应用密钥或签名逻辑放进小程序代码。
  • 不要只凭 my.tradePay 的 success 回调判断付款成功。应查询服务端状态,并结合支付宝异步通知确认结果。
  • 用户取消支付、网络中断或收银台超时,不一定意味着交易失败,仍需主动查询交易状态。
  • 游客订单接口同样需要鉴权、限流、防重放和越权检查。
  • 如果使用支付宝用户标识,应遵守最小必要原则,并确认应用权限、隐私政策和接口字段符合当前开放平台要求。

实现“未登录、未授权也能支付”,并不是绕过授权,而是把会员登录和支付流程拆开。小程序先通过游客会话创建业务订单,由服务端安全地创建支付宝交易,再使用 my.tradePay 拉起收银台。

备注:内容仅供参考。