PAYATHON 2026

鸿蒙支付宝支付成功后未回调客户端

支付老李

结论

支付成功但客户端没有收到结果,通常不代表交易失败,更可能是“支付宝返回应用”这条链路没有处理好。建议重点检查以下三项:

  1. 发起支付的 UIAbility 是否正确接收了返回的 Want,特别是应用已经运行时触发的 onNewWant()。
  2. 支付回调、Promise 或事件监听是否在返回前被销毁、覆盖或重复注册。
  3. module.json5 中用于唤起应用的 URI、包名和签名等配置,是否与支付宝开放平台及 SDK 配置一致。

客户端回调只能用来更新页面状态。订单是否支付成功,最终应由服务端根据支付宝异步通知或主动查询结果确认。

为什么支付成功却没有客户端回调

支付宝收银台完成付款后,交易处理和应用唤起是两条相对独立的链路:

  • 交易已经完成时,支付宝内部日志会显示支付成功。
  • 如果返回链路失败,应用可能没有被唤起,也可能已经唤起,但返回参数没有传给 SDK。
  • 应用已经运行时,系统通常不会再次执行 onCreate(),而是将新的 Want 交给 onNewWant()。
  • 页面、组件或回调对象提前销毁,也可能出现 SDK 已经返回结果,但业务代码没有收到通知的情况。

所以,SDK 内部出现支付成功日志,不等于业务层回调已经执行。

排查步骤

1. 确认应用是否被支付宝重新唤起

分别在 UIAbility 的 onCreate()、onNewWant() 和 onForeground() 中记录日志:

import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    console.info('[PayDebug] EntryAbility.onCreate');
    console.info(`[PayDebug] want.uri=${want.uri ?? ''}`);
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    console.info('[PayDebug] EntryAbility.onNewWant');
    console.info(`[PayDebug] want.uri=${want.uri ?? ''}`);
  }

  onForeground(): void {
    console.info('[PayDebug] EntryAbility.onForeground');
  }
}

测试时需要覆盖这些场景:

  • 应用留在前台时发起支付;
  • 应用进入后台后完成支付;
  • 应用进程被系统回收后完成支付;
  • 已安装和未安装支付宝客户端。

如果支付后没有出现任何生命周期日志,先检查应用跳转 URI、包名、签名和 module.json5 配置。如果 onNewWant() 已经执行,但业务回调没有执行,再检查返回参数是否传给了支付宝 SDK。

2. 在 onNewWant() 中处理返回参数

应用已经运行时,支付返回信息通常会通过新的 Want 送达,因此不能只在 onCreate() 中初始化或解析。

不同版本的支付宝鸿蒙 SDK 可能使用不同的返回处理 API,具体应以当前接入版本的官方文档为准。代码结构可以统一成下面这样:

export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.handlePayReturn(want);
  }

  onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    this.handlePayReturn(want);
  }

  private handlePayReturn(want: Want): void {
    console.info(`[PayDebug] receive pay return: ${want.uri ?? ''}`);

    // 在这里调用当前支付宝 SDK 提供的返回参数处理接口。
    // 不要自行猜测或修改支付宝返回参数。
  }
}

如果 SDK 文档要求将 Want、URI 或回调参数传给指定接口,应在两个入口中调用同一套处理逻辑。

3. 检查支付调用的异步处理

如果 SDK 的支付接口返回 Promise,需要同时处理成功结果和异常,避免异常被静默忽略:

async function startPay(orderInfo: string): Promise<void> {
  try {
    // pay() 的导入方式和参数以实际使用的支付宝 SDK 为准。
    const result = await alipay.pay(orderInfo);

    console.info(`[PayDebug] pay result=${JSON.stringify(result)}`);
    handleClientPayResult(result);
  } catch (error) {
    console.error(`[PayDebug] pay failed: ${JSON.stringify(error)}`);
    showPayPendingAndQueryOrder();
  }
}

不要在调用支付接口后立即销毁页面,也不要忽略返回的 Promise:

// 不推荐:调用结果没有被等待,也没有异常处理
alipay.pay(orderInfo);
router.back();

如果使用事件监听,还要确认监听器没有在页面的 aboutToDisappear()、onPageHide() 等生命周期中过早注销。

4. 核对应用唤起配置

检查 module.json5 中目标 UIAbility 的 skills 配置。URI 的 scheme、host、path 等字段必须与实际回跳地址一致。

以下配置仅作示意,具体值不能直接照用:

{
  "abilities": [
    {
      "name": "EntryAbility",
      "exported": true,
      "skills": [
        {
          "actions": [
            "ohos.want.action.viewData"
          ],
          "entities": [
            "entity.system.browsable"
          ],
          "uris": [
            {
              "scheme": "your-app-scheme",
              "host": "pay-result"
            }
          ]
        }
      ]
    }
  ]
}

还需要检查:

  • 调试包和正式包是否使用了不同的包名或签名;
  • 支付宝开放平台配置是否对应当前应用;
  • 回跳 URI 的大小写、路径和特殊字符是否完全一致;
  • 目标 UIAbility 是否允许外部唤起;
  • 是否有多个 UIAbility 同时匹配同一个 URI,导致返回信息进入错误页面。

5. 确认回调对象没有失效

支付期间,用户会暂时离开当前应用,原页面可能被销毁或重新创建。因此,不要只将回调保存在页面组件的临时变量中。

可以将订单状态暂存在应用级状态中,等应用恢复后再查询:

interface PendingPayOrder {
  orderNo: string;
  startedAt: number;
}

let pendingPayOrder: PendingPayOrder | undefined;

function rememberPayOrder(orderNo: string): void {
  pendingPayOrder = {
    orderNo,
    startedAt: Date.now()
  };
}

async function resumePendingPayment(): Promise<void> {
  if (!pendingPayOrder) {
    return;
  }

  // 请求业务服务端查询订单状态。
  await queryOrderStatus(pendingPayOrder.orderNo);
}

推荐的兜底处理

客户端恢复到前台后,如果在合理时间内仍未收到支付回调,应显示“支付结果确认中”,并向业务服务端查询订单状态,不要直接提示支付失败:

async function confirmPayment(orderNo: string): Promise<void> {
  const order = await queryOrderStatus(orderNo);

  switch (order.status) {
    case 'PAID':
      showPaySuccess();
      break;
    case 'CLOSED':
      showPayFailed();
      break;
    default:
      showPayPending();
      break;
  }
}

服务端需要校验支付宝异步通知的签名、商户订单号、金额、商户身份和交易状态,同时保证通知处理具有幂等性。

注意事项

  • 不要仅根据客户端返回字段将订单标记为已支付。
  • 不要把支付宝 SDK 日志中的“支付成功”视为业务服务端已经确认到账。
  • 不要在日志中完整输出订单字符串、签名、用户标识或支付凭证。
  • 如果问题只出现在混淆后的正式包中,应检查支付宝 SDK 相关类是否需要配置保留规则。
  • 如果 onNewWant() 已经收到返回信息,但传给 SDK 后仍然没有业务回调,需要记录 SDK 版本、HarmonyOS 版本、设备型号、应用前后台状态和脱敏后的 Want 信息,再向支付宝技术支持核对该版本的接入要求。

备注:内容仅供参考。