PAYATHON 2026

支付宝小程序通过jsapi收款后如何实现支付宝app语音播报

支付老李

结论

通过 my.tradePay 等 JSAPI 完成在线支付时,小程序无法要求商家手机上的支付宝 App 播放“到账语音”。收款语音是支付宝客户端的内置能力,是否播报取决于交易产品、商家账号、客户端设置和支付宝当前规则,不属于 JSAPI 开放功能。

因此,不能把支付宝 App 的语音播报当作 JSAPI 收款成功的通知机制。更可靠的方案是:服务端接收支付宝异步通知,验签并确认交易成功,再向商家端 App、收银设备或语音盒子推送消息,由商家端播放到账语音。

为什么不能依赖前端支付回调播报

小程序调用支付接口后,通常只能获得付款人一侧的支付结果,例如:

my.tradePay({
  tradeNO: tradeNo,
  success: (result) => {
    console.log(result);
  },
  fail: (error) => {
    console.error(error);
  }
});

这个回调发生在付款人的小程序中,有以下限制:

  • 付款人关闭页面后,回调可能无法继续处理。
  • 回调结果不能代替服务端对交易状态的确认。
  • 小程序无法控制商家手机上的支付宝 App。
  • 商家端支付宝 App 是否播报,由支付宝客户端自行决定。
  • 小程序进入后台后,页面脚本通常无法稳定播放语音。

即使付款端显示支付成功,也不应直接依据这个前端结果通知商家。

推荐实现流程

可以按照下面的链路实现:

  1. 服务端创建支付宝交易,并设置异步通知地址 notify_url。
  2. 付款人在小程序中通过 my.tradePay 完成支付。
  3. 支付宝向 notify_url 发送异步通知。
  4. 服务端使用支付宝公钥验签。
  5. 核对 app_id、seller_id、订单号、金额和交易状态。
  6. 使用 out_trade_no 或 trade_no 做幂等处理。
  7. 将支付成功消息推送到对应商家的收银端。
  8. 商家端收到消息后播放“支付宝到账 XX 元”。
  9. 服务端处理完成后,向支付宝返回纯文本 success。

推送通道可以使用 WebSocket、厂商系统推送、连接消息队列的收银客户端或专用语音播报设备。

服务端处理异步通知

下面以 Java 为例说明主要逻辑。具体的依赖版本、异常类型和配置方式,应以项目使用的支付宝 SDK 文档为准。

@PostMapping("/alipay/notify")
@ResponseBody
public String handleAlipayNotify(HttpServletRequest request) {
    Map<String, String> params = new HashMap<>();

    request.getParameterMap().forEach((key, values) -> {
        if (values != null && values.length > 0) {
            params.put(key, values[0]);
        }
    });

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

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

        String appId = params.get("app_id");
        String sellerId = params.get("seller_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");

        // 必须与本系统创建订单时保存的数据核对
        Order order = orderService.findByOutTradeNo(outTradeNo);
        if (order == null) {
            return "failure";
        }

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

        if (!expectedSellerId.equals(sellerId)) {
            return "failure";
        }

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

        if ("TRADE_SUCCESS".equals(tradeStatus)
                || "TRADE_FINISHED".equals(tradeStatus)) {

            // 内部必须做幂等控制,避免重复通知导致重复播报
            boolean firstSuccess = orderService.markPaidIfUnpaid(
                outTradeNo,
                tradeNo
            );

            if (firstSuccess) {
                merchantPushService.sendPaymentSuccess(
                    order.getMerchantId(),
                    outTradeNo,
                    notifiedAmount
                );
            }
        }

        return "success";
    } catch (Exception e) {
        // 记录必要日志,但不要泄露私钥、完整请求或敏感信息
        return "failure";
    }
}

支付宝可能重复发送异步通知,因此“更新订单”和“生成播报事件”都必须保证幂等。不能每收到一次通知,就无条件播放一次语音。

商家端播放语音

如果商家使用自有 Android 收银 App,可以在收到服务端推送后调用系统 TTS。下面只展示简化后的逻辑:

public class PaymentSpeaker {
    private final TextToSpeech textToSpeech;

    public PaymentSpeaker(Context context) {
        textToSpeech = new TextToSpeech(context, status -> {
            if (status == TextToSpeech.SUCCESS) {
                textToSpeech.setLanguage(Locale.SIMPLIFIED_CHINESE);
            }
        });
    }

    public void speak(BigDecimal amount, String eventId) {
        String message = "支付宝到账"
            + amount.setScale(2, RoundingMode.HALF_UP).toPlainString()
            + "元";

        textToSpeech.speak(
            message,
            TextToSpeech.QUEUE_ADD,
            null,
            eventId
        );
    }

    public void release() {
        textToSpeech.shutdown();
    }
}

实际项目还要处理以下问题:

  • 设备静音、媒体音量和勿扰模式。
  • TTS 初始化失败或缺少中文语音包。
  • 推送消息重复、乱序和断线重连。
  • 多笔款项同时到账时的语音排队。
  • 金额格式及超大金额的朗读方式。
  • App 被系统清理后的通知到达率。
  • 商户、门店与收银设备之间的绑定关系。

如果商家使用普通小程序作为收银端,还要考虑小程序进入后台后的限制。小程序不适合承担持续、可靠的语音播报。对可靠性要求较高的场景,更适合使用原生收银 App、常驻收银终端或专用语音设备。

能否继续使用支付宝 App 自带播报

可以先查看商家支付宝 App 中是否有“收钱到账语音提醒”等设置,并确认通知权限、音量和后台运行权限已经开启。不过,即使这些设置都正确,也不能保证所有 JSAPI 在线交易都会触发播报。

如果业务允许改用支付宝收钱码、当面付等线下收款产品,可以使用真实商户账号和设备验证支付宝的原生播报效果。具体是否支持,仍要以当前接入产品的官方说明和实际测试结果为准,不能只根据二维码支付和 JSAPI 支付的表面差异判断。

注意事项

  • 应以支付宝服务端异步通知作为支付结果的主要依据,不能只依赖 my.tradePay 的前端回调。
  • 验签通过后,仍需核对 app_id、收款方、订单号、订单金额和交易状态。
  • 私钥只能保存在服务端,不能写入小程序代码。
  • 支付通知接口必须使用 HTTPS,同时做好访问日志和异常告警。
  • 播报事件必须绑定正确的商户和门店,避免把订单消息推送到错误设备。
  • 支付结果确认之前,不能播报“到账”。
  • 如果支付宝没有开放对应的原生语音接口,就无法通过 JSAPI、URL Scheme 或前端脚本强制触发支付宝 App 播报。

支付宝负责确认支付结果,业务系统负责将结果可靠地发送到商家端,商家端负责播放语音。这套方案可以覆盖 JSAPI 在线支付,也不依赖支付宝 App 的原生播报策略。

备注:内容仅供参考。