电脑支付 Java 服务端如何在 notify_url 中获取额外参数
结论
business_params 不能用作 notify_url 的自定义参数回传通道。它是特定业务的扩展字段,支付宝异步通知不会原样返回其中的购买人 ID、售卖人 ID 等信息。
一般有两种处理方式:
- 建议把业务数据保存在本地数据库中,通过
out_trade_no关联支付订单。收到通知后,再根据out_trade_no查询订单。 - 如果确实需要支付宝原样回传少量信息,可以使用
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_nototal_amountapp_idseller_id,如果当前接入模式会返回该字段trade_status
比较金额时应使用 BigDecimal,不要使用 double。
正确返回处理结果
处理成功后,响应正文应返回:
success
验签失败或业务处理失败时返回:
failure
不要返回 JSON、HTML 页面或其他说明,否则支付宝可能无法识别处理结果,并继续重试通知。
更稳妥的设计是让 out_trade_no 负责关联订单,把完整业务信息保存在本地。只有确有需要时,才用 passback_params 传递少量且不敏感的辅助数据。
备注:内容仅供参考。