PAYATHON 2026

支付宝支付成功但未收到回调和交易号

支付老李

结论

先检查回调地址。题目中填写的是:

https://103d434.r17.cpolar.top/alipay/ notify

/alipay/ 和 notify 之间多了一个空格,可能导致支付宝请求错误路径、访问 URL 编码后的路径,或者回调地址无法通过校验。正确地址通常是:

https://103d434.r17.cpolar.top/alipay/notify

支付宝支付成功,并不表示商户系统已经收到异步通知。交易号 202503252200453580505444842 已存在,说明支付宝侧很可能已经生成了交易记录。建议先调用 alipay.trade.query 查询交易状态,再依次排查公网访问、路由、签名验证和响应内容。

为什么没有收到回调

可能的原因有:

  • notify_url 中包含空格、换行或多余字符。
  • cpolar 临时域名已经失效,或者隧道地址发生了变化。
  • /alipay/notify 路由不存在,返回 404、301、302 或 500。
  • 服务只支持浏览器访问,没有正确接收支付宝发送的 POST 表单。
  • 防火墙、网关、反向代理或安全策略拦截了请求。
  • HTTPS 证书异常,或者域名无法从公网正常访问。
  • 程序已经收到通知,但验签失败后直接退出。
  • 回调处理成功后没有返回纯文本 success,支付宝因此继续重试。
  • 业务代码只从同步跳转页面读取 trade_no,没有从异步通知或查询接口获取。
  • 创建支付订单时没有正确传入 notify_url。只在浏览器跳转地址中配置回调路径不会生效。

第一步:修正并验证回调地址

创建支付请求时,将异步通知地址设置为不含空格的完整公网 URL:

https://103d434.r17.cpolar.top/alipay/notify

然后从公网环境测试这个地址:

curl -i -X POST \
  'https://103d434.r17.cpolar.top/alipay/notify' \
  -d 'test=1'

检查以下几项:

  • 域名是否可以正常解析和访问。
  • 请求是否发生重定向。
  • 应用能否收到 POST 请求。
  • Web 服务器是否返回 404 或 500。
  • cpolar 隧道是否仍指向正确的本地端口。
  • 路由的大小写、末尾斜杠是否与程序配置一致。

这条测试请求没有支付宝签名,业务程序可以拒绝处理,但服务器访问日志中必须能看到它。

cpolar 通常适合开发调试。如果隧道重启后域名发生变化,需要同步更新支付请求中的 notify_url。生产环境应使用稳定的公网域名和有效的 HTTPS 证书。

第二步:通过查询接口确认订单状态

不要只等异步通知。可以调用 alipay.trade.query,按支付宝交易号查询:

{
  "trade_no": "202503252200453580505444842"
}

如果系统保存了商户订单号,也可以使用 out_trade_no 查询:

{
  "out_trade_no": "商户创建支付时使用的订单号"
}

需要查看的返回字段包括:

  • trade_no:支付宝交易号。
  • out_trade_no:商户订单号。
  • trade_status:交易状态。
  • buyer_pay_amount、total_amount:金额字段,具体返回情况以实际接口结果为准。

只有查询结果明确显示交易成功,而且商户订单号、应用 ID、金额等信息一致,才能把本地订单更新为已支付。不能仅凭用户提供的 trade_no 修改订单状态。

第三步:正确处理异步通知

支付宝异步通知通常通过 POST 表单参数发送,不一定是 JSON。处理顺序如下:

  1. 读取全部表单参数。
  2. 使用支付宝公钥验证签名。
  3. 校验 app_id、seller_id、out_trade_no 和金额。
  4. 判断 trade_status。
  5. 以幂等方式更新本地订单。
  6. 处理成功后返回纯文本 success。

下面是示例逻辑。具体的 SDK 方法名以项目使用的编程语言和 SDK 版本为准:

POST /alipay/notify

params = read_form_parameters()

if not verify_alipay_signature(params):
    return "failure"

order = find_order(params["out_trade_no"])

if order does not exist:
    return "failure"

if params["app_id"] != configured_app_id:
    return "failure"

if params["seller_id"] != configured_seller_id:
    return "failure"

if decimal(params["total_amount"]) != order.amount:
    return "failure"

if params["trade_status"] in ["TRADE_SUCCESS", "TRADE_FINISHED"]:
    update_order_paid_idempotently(
        out_trade_no=params["out_trade_no"],
        trade_no=params["trade_no"]
    )

return "success"

响应内容应保持简单:

success

不要返回 HTML、JSON、调试信息或多余空白,也不要在处理完成前提前返回 success。

第四步:检查日志位置

至少要检查三层日志:

  • cpolar 请求记录,用于确认支付宝的请求是否到达公网隧道。
  • Nginx、Apache 或应用网关日志,用于确认请求路径和状态码。
  • 应用日志,用于确认参数解析、验签和数据库更新是否成功。

日志中可以记录以下信息,但不要写入私钥、完整签名密钥或其他敏感凭据:

request_time
request_path
out_trade_no
trade_no
trade_status
app_id
total_amount
signature_verify_result
response_status

如果 cpolar 中没有请求记录,问题通常出在 notify_url、公网连通性,或者支付宝尚未发送通知。如果网关有记录而应用没有,应检查反向代理和路由配置。如果应用收到了请求,但订单没有更新,则应检查验签、金额校验、事务和幂等逻辑。

交易号应从哪里获取

支付宝交易号有两个可靠来源:

  • 异步通知参数中的 trade_no。
  • alipay.trade.query 返回结果中的 trade_no。

同步跳转参数只适合页面展示或辅助提示,不能作为确认支付成功的最终依据。用户可能关闭页面,浏览器跳转也可能被网络或前端逻辑中断,而异步通知和主动查询仍可完成订单确认。

注意事项

  • 验签必须使用支付宝公钥,不能使用应用公钥或应用私钥。
  • 验签前不要修改、转义或重新编码通知参数。
  • 比较金额时应使用十进制定点数,避免直接使用二进制浮点数。
  • 支付宝可能重复发送回调,因此订单更新必须具备幂等性。
  • 回调处理应尽量快速。耗时业务可以在订单状态写入数据库后交给队列处理。
  • 修正 notify_url 只会影响之后通过该地址创建的支付请求。对于已经支付的订单,应调用 alipay.trade.query 主动补查,并根据校验结果补单。
  • 交易号属于业务敏感信息。排查完成后,不应继续在公开页面展示完整号码。

备注:内容仅供参考。