支付宝支付成功但未收到回调和交易号
结论
先检查回调地址。题目中填写的是:
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。处理顺序如下:
- 读取全部表单参数。
- 使用支付宝公钥验证签名。
- 校验
app_id、seller_id、out_trade_no和金额。 - 判断
trade_status。 - 以幂等方式更新本地订单。
- 处理成功后返回纯文本
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主动补查,并根据校验结果补单。 - 交易号属于业务敏感信息。排查完成后,不应继续在公开页面展示完整号码。
备注:内容仅供参考。
