支付宝支付后未收到异步回调,如何确认推送状态?
结论
仅凭 out_trade_no=6898896632557862913 和回调地址,无法确认支付宝是否已经推送通知,也无法判断推送过程中是否报错。需要同时核对以下信息:
- 支付宝侧的交易状态和异步通知记录。
- 接收服务器、网关及应用日志中是否出现支付宝发出的
POST请求。 - 应用是否正确完成验签和业务校验,并返回了正确内容。
交易支付成功不代表异步通知已经被业务系统成功处理。支付宝可能已经发出通知,但接口超时、响应内容不是纯文本 success、发生重定向、被 WAF 拦截或验签失败。在这些情况下,支付宝通常会将通知视为失败。
首先确认交易状态
使用支付宝交易查询接口 alipay.trade.query,将商户订单号作为字符串传入:
{
"out_trade_no": "6898896632557862913"
}
重点检查返回的 trade_status:
TRADE_SUCCESS:交易支付成功。TRADE_FINISHED:交易已结束。WAIT_BUYER_PAY:尚未支付。TRADE_CLOSED:交易已关闭。
交易查询只能确认支付结果,不能证明异步通知已经送达。业务系统也不应只依赖回调。订单长时间未更新时,可以主动调用查询接口进行补偿。
out_trade_no 应始终以字符串形式保存和传输。该值超过 JavaScript 的安全整数范围,转换为 Number 可能导致精度丢失。
检查服务器是否收到请求
接收地址为:
https://www.eeeknow.com/payCallback/alipayCallback
先根据支付发生时间检查负载均衡、CDN、WAF、Nginx 和应用日志。可以按路径筛选 Nginx 日志:
grep '/payCallback/alipayCallback' /var/log/nginx/access.log
如果日志记录了请求参数,也可以搜索订单号:
grep '6898896632557862913' /var/log/nginx/access.log
至少需要检查以下内容:
- 请求时间和来源 IP。
- HTTP 方法是否为
POST。 - HTTP 状态码。
- 请求处理耗时。
- 是否出现
301、302、403、404、499或5xx。 - 请求是否进入应用。
- 验签和订单处理是否成功。
- 最终是否返回纯文本
success。
如果网关有请求记录而应用没有,通常需要排查路由、反向代理、WAF 或访问控制。如果应用收到了请求但订单没有更新,则应重点检查参数解析、验签和业务校验。
正确接收支付宝通知
支付宝异步通知通常以表单参数提交,不能按 JSON 请求体处理。下面是 Java Spring Boot 示例:
import com.alipay.api.internal.util.AlipaySignature;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
public class AlipayCallbackController {
@Value("${alipay.public-key}")
private String alipayPublicKey;
@PostMapping(
value = "/payCallback/alipayCallback",
produces = MediaType.TEXT_PLAIN_VALUE
)
public String callback(HttpServletRequest request) {
Map<String, String> params = new HashMap<>();
request.getParameterMap().forEach((name, values) -> {
if (values != null && values.length > 0) {
params.put(name, String.join(",", values));
}
});
try {
boolean verified = AlipaySignature.rsaCheckV1(
params,
alipayPublicKey,
"UTF-8",
"RSA2"
);
if (!verified) {
return "failure";
}
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");
String appId = params.get("app_id");
String sellerId = params.get("seller_id");
// 1. 根据 outTradeNo 查询本地订单。
// 2. 校验 appId、sellerId、totalAmount 与本地订单一致。
// 3. 仅处理预期的 tradeStatus。
// 4. 使用事务或唯一约束保证重复通知不会重复入账。
// 5. 保存 tradeNo,更新订单状态并提交事务。
if ("TRADE_SUCCESS".equals(tradeStatus)
|| "TRADE_FINISHED".equals(tradeStatus)) {
return "success";
}
return "failure";
} catch (Exception e) {
// 记录异常,但不要在日志中泄露私钥或完整敏感参数。
return "failure";
}
}
}
配置项 alipay.public-key 必须填写支付宝公钥,不能使用应用公钥或应用私钥。具体公钥要与当前应用、签名模式及密钥配置一致。
回调处理的要求
准确返回处理结果
处理成功后直接返回:
success
不要返回 JSON、HTML 或其他提示文字,例如:
{"code":200,"message":"success"}
即使 HTTP 状态码为 200,上述 JSON 也可能不会被识别为成功响应。还要检查统一响应包装器,避免它将字符串包装成 JSON。
不要重定向
支付宝配置的地址应直接处理通知,不能依赖以下跳转:
HTTP 301
HTTP 302
HTTP 307
HTTP 308
HTTP 跳转 HTTPS、域名规范化以及自动补充路径末尾的 /,都可能影响通知处理。应直接配置最终可访问的 HTTPS 地址。
完成业务事务后再返回成功
只有验签通过、订单校验完成且数据库事务成功提交后,才能返回 success。如果先返回成功,再异步更新订单,后续处理一旦失败,可能无法通过通知重试恢复。
同一笔交易可能收到多次通知,因此接口必须支持幂等处理。可以为 out_trade_no 或 trade_no 设置唯一约束。订单已经处理成功时,直接返回 success。
验签后继续校验业务参数
验签通过后,还需要核对:
out_trade_no是否存在。app_id是否属于当前应用。seller_id是否为预期收款账号。total_amount是否与订单金额一致。trade_status是否属于业务允许处理的状态。- 订单是否已经处理,避免重复入账。
金额建议使用 BigDecimal 比较,不要使用 double:
BigDecimal notifiedAmount = new BigDecimal(params.get("total_amount"));
BigDecimal orderAmount = order.getTotalAmount();
if (notifiedAmount.compareTo(orderAmount) != 0) {
return "failure";
}
排查接口可用性
可以从公网测试接口的网络连通性:
curl -i -X POST \
'https://www.eeeknow.com/payCallback/alipayCallback' \
-H 'Content-Type: application/x-www-form-urlencoded' \
--data 'out_trade_no=6898896632557862913'
这个请求没有支付宝的有效签名,因此业务接口返回 failure 或提示验签失败是正常现象。该测试用于确认:
- 域名能否解析。
- TLS 证书是否有效,证书链是否完整。
- 接口是否超时或发生重定向。
- 请求能否进入网关和应用。
- 日志能否记录该请求。
不要为了让测试请求通过而关闭验签。
常见原因
如果日志中完全没有请求记录,优先检查:
- 支付时传入的
notify_url是否确实是当前地址。 - 该地址是否允许公网直接访问。
- DNS、HTTPS 证书和证书链是否正常。
- 防火墙、CDN、WAF 或 IP 白名单是否拦截了请求。
- 支付宝侧是否有通知发送或失败记录。
如果有请求记录但订单没有更新,优先检查:
- 是否将表单请求错误地按 JSON 解析。
- 是否使用了错误的支付宝公钥。
charset或sign_type是否与实际配置一致。- 是否在取参后修改了内容,又使用修改后的参数验签。
- 订单号是否被转换为数值并丢失精度。
- 金额、应用 ID 或卖家账号是否校验失败。
- 数据库更新是否异常,事务是否回滚。
- 统一异常处理是否返回了 HTML 错误页。
- 实际响应是否为 JSON,而不是纯文本
success。 - 接口处理时间是否过长,导致支付宝侧超时。
最终确认方式
由有权限的技术人员根据支付时间和订单号,分别导出以下证据:
alipay.trade.query返回的交易状态。- 支付宝侧的异步通知记录及响应结果。如果当前后台没有相关入口,需要向支付宝技术支持提供应用信息、订单号和支付时间,请其协助查询。
- 网关访问日志中的请求时间、状态码和耗时。
- 应用日志中的验签结果、业务校验结果和响应内容。
- 数据库中该订单的处理状态及更新时间。
将这些记录按时间和订单号对应起来,才能准确判断支付宝没有发出通知、通知没有到达服务器,还是服务器收到通知后处理或响应失败。
备注:内容仅供参考。
