PAYATHON 2026

电脑支付 Java 服务端如何在 notify_url 中获取额外参数

支付阿杰

结论

business_params 不能用作 notify_url 的自定义参数回传通道。它是特定业务的扩展字段,支付宝异步通知不会原样返回其中的购买人 ID、售卖人 ID 等信息。

一般有两种处理方式:

  1. 建议把业务数据保存在本地数据库中,通过 out_trade_no 关联支付订单。收到通知后,再根据 out_trade_no 查询订单。
  2. 如果确实需要支付宝原样回传少量信息,可以使用 passback_params,但要在提交支付请求前进行 URL 编码。

为什么获取不到 business_params

支付宝异步通知只包含接口约定的通知参数,不会完整返回请求中的所有 biz_content 字段。

business_params 通常用于双方约定的特殊业务场景,并不是通用的自定义参数字段。支付请求被正常受理,也不代表该字段会出现在 notify_url 中。

异步通知通过以下字段关联本地订单:

out_trade_no

购买人 ID、售卖人 ID、商品 ID 等业务信息,应在创建支付订单时存入本地数据库,不要依赖支付回调传递。

推荐方案:使用 out_trade_no 查询本地订单

创建支付请求前,先生成唯一的商户订单号,并把相关业务数据保存到本地。

String outTradeNo = generateOrderNo();

LocalOrder order = new LocalOrder();
order.setOutTradeNo(outTradeNo);
order.setBuyerId(buyerId);
order.setSellerId(sellerId);
order.setAmount(amount);
order.setStatus("WAIT_PAY");

orderService.create(order);

JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", outTradeNo);
bizContent.put("total_amount", amount);
bizContent.put("subject", "商品订单");

AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setNotifyUrl(notifyUrl);
request.setReturnUrl(returnUrl);
request.setBizContent(bizContent.toJSONString());

收到异步通知后,先完成验签,再通过 out_trade_no 查询订单:

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

    request.getParameterMap().forEach((key, values) -> {
        params.put(key, String.join(",", values));
    });

    try {
        boolean verified = AlipaySignature.rsaCheckV1(
            params,
            alipayPublicKey,
            charset,
            signType
        );

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

        String outTradeNo = request.getParameter("out_trade_no");
        String tradeNo = request.getParameter("trade_no");
        String tradeStatus = request.getParameter("trade_status");
        String totalAmount = request.getParameter("total_amount");

        LocalOrder order = orderService.findByOutTradeNo(outTradeNo);
        if (order == null) {
            return "failure";
        }

        // 还应校验金额、app_id、seller_id 等关键字段
        if (!order.getAmount().toPlainString().equals(totalAmount)) {
            return "failure";
        }

        if ("TRADE_SUCCESS".equals(tradeStatus)
                || "TRADE_FINISHED".equals(tradeStatus)) {
            orderService.markPaidIdempotently(
                outTradeNo,
                tradeNo
            );
        }

        return "success";
    } catch (AlipayApiException e) {
        return "failure";
    }
}

本地保存业务数据,不受回传参数长度、编码方式和字段支持范围的影响,也能减少内部用户信息在第三方支付链路中的暴露。

需要回传少量信息时使用 passback_params

如果业务确实需要从异步通知中取得自定义数据,可以使用 passback_params。提交时要把它放在 biz_content 中。

JSONObject passbackData = new JSONObject();
passbackData.put("buyerId", buyerId);
passbackData.put("sellerId", sellerId);

String passbackParams = URLEncoder.encode(
    passbackData.toJSONString(),
    StandardCharsets.UTF_8.name()
);

JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", outTradeNo);
bizContent.put("total_amount", amount);
bizContent.put("subject", "商品订单");
bizContent.put("passback_params", passbackParams);

AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setNotifyUrl(notifyUrl);
request.setReturnUrl(returnUrl);
request.setBizContent(bizContent.toJSONString());

在异步通知中读取:

String passbackParams = request.getParameter("passback_params");

if (passbackParams != null && !passbackParams.isEmpty()) {
    String decoded = URLDecoder.decode(
        passbackParams,
        StandardCharsets.UTF_8.name()
    );

    JSONObject data = JSON.parseObject(decoded);
    String buyerId = data.getString("buyerId");
    String sellerId = data.getString("sellerId");
}

不同 Servlet 容器或 Web 框架处理请求参数时,解码行为可能不同。如果取得的 passback_params 已经是完整 JSON,就不要再次调用 URLDecoder.decode,以免重复解码。具体应以实际收到的原始通知内容为准。

注意事项

先验签,再处理业务

验签前不要修改、删除或重新编码通知参数。先使用支付宝公钥对完整通知参数验签,成功后再读取和处理业务字段。

不要信任回传的自定义参数

passback_params 只能辅助关联数据,不能取代本地数据库中的可信记录。购买人、售卖人、订单金额和商品归属等信息,仍应以本地订单为准。

回调处理必须幂等

支付宝可能会多次发送同一笔交易的异步通知。更新订单时,应使用条件更新、唯一约束或事务锁,防止重复发货和重复记账。

例如:

UPDATE orders
SET status = 'PAID',
    alipay_trade_no = ?
WHERE out_trade_no = ?
  AND status = 'WAIT_PAY';

只有受影响行数为 1 时,才执行后续的一次性业务。

核对关键通知字段

验签成功不代表通知一定对应目标订单。至少还要核对以下字段:

  • out_trade_no
  • total_amount
  • app_id
  • seller_id,如果当前接入模式会返回该字段
  • trade_status

比较金额时应使用 BigDecimal,不要使用 double。

正确返回处理结果

处理成功后,响应正文应返回:

success

验签失败或业务处理失败时返回:

failure

不要返回 JSON、HTML 页面或其他说明,否则支付宝可能无法识别处理结果,并继续重试通知。

更稳妥的设计是让 out_trade_no 负责关联订单,把完整业务信息保存在本地。只有确有需要时,才用 passback_params 传递少量且不敏感的辅助数据。

备注:内容仅供参考。