PAYATHON 2026

沙箱支付成功后测试仍显示“等待用户完成支付”

支付老李

结论

后台返回 {Status:1, Message:OK},只能说明商户系统认为支付处理成功,不能证明测试平台已经识别出这笔交易属于当前测试用例。

如果测试仍停留在【等待用户完成支付】,通常有两种情况:测试平台没有关联到这笔交易,或者支付接口虽然调用成功,但支付宝业务状态尚未达到最终成功状态。建议重点核对测试环境、应用身份、订单号、支付账号和 trade_status,再用全新的订单重新走一遍完整支付流程。

为什么后台成功,测试步骤却没有通过

测试平台与商户后台使用两套独立的状态判断机制:

  • 商户后台通过同步跳转、异步通知或主动查询更新订单状态。
  • 测试平台只认可在当前测试任务中,通过指定沙箱应用和沙箱账号完成的交易。
  • 自定义返回值中的 Status:1 和 Message:OK 不是支付宝标准交易状态,可能只表示接口请求成功,或商户程序执行成功。
  • 判断交易是否完成,应以 alipay.trade.query 返回的 trade_status 为准。

支付宝交易的最终业务状态通常应为:

TRADE_SUCCESS

或:

TRADE_FINISHED

如果只返回:

code=10000
msg=Success

通常只能说明接口调用成功,还需要继续检查 trade_status。

优先排查的项目

1. 确认使用的是同一个沙箱应用

创建支付订单和查询订单时,必须使用当前测试用例关联的沙箱应用,包括:

  • 相同的沙箱 app_id
  • 相同的商户私钥
  • 与该应用匹配的支付宝公钥或证书
  • 沙箱网关
  • 当前沙箱应用提供的买家账号

沙箱网关通常配置为:

https://openapi-sandbox.dl.alipaydev.com/gateway.do

如果支付发生在正式环境或另一个沙箱应用中,或者使用的账号不属于当前测试环境,那么即使交易成功,当前测试用例也可能无法识别。

2. 检查订单是否属于当前测试任务

不要使用历史订单号,也不要重复使用已经支付成功的 out_trade_no。每次重新测试时,都应生成一个全新的商户订单号,例如:

TEST_20240408161259_001

同时确认页面上实际支付的订单号与后台查询的订单号完全一致。本例需要核对:

out_trade_no = 202404087183014128443719680
trade_no     = 2024040822001416970502542164

如果用户支付的是订单 A,后台查询的却是订单 B,后台可能仍然能查到一笔成功交易,但测试平台不会推进当前步骤。

3. 不要只判断自定义的 Status

下面的结果:

{Status:1 OutTradeNo:202404087183014128443719680 TradeNo:2024040822001416970502542164 Amount:9.90 Message:OK Subject:}

是业务系统封装后的结构,并非支付宝接口的完整原始响应。应记录并检查支付宝返回的这些字段:

code
sub_code
sub_msg
out_trade_no
trade_no
trade_status
total_amount

只有 trade_status 确认为 TRADE_SUCCESS 或 TRADE_FINISHED,才能认定支付宝侧的交易已经完成。

4. 在当前测试页面发起并完成支付

建议按以下顺序重新操作:

  1. 停止当前已卡住的测试。
  2. 重新启动测试用例。
  3. 从当前测试步骤生成新的支付链接。
  4. 使用指定的沙箱买家账号完成支付。
  5. 支付完成后,不要立即关闭浏览器、手机页面或中间跳转页。
  6. 等待页面完成 return_url 跳转。
  7. 确认服务端已经收到 notify_url 通知并完成验签。
  8. 使用相同的 out_trade_no 调用交易查询接口。
  9. 返回测试平台,观察步骤是否推进。

如果支付链接来自历史日志、收藏夹或其他测试任务,应废弃该链接并重新创建订单。

交易查询判断示例

下面是 Go 风格的判断示例,字段名称需要根据实际使用的 SDK 调整:

resp, err := client.TradeQuery(outTradeNo)
if err != nil {
    return fmt.Errorf("查询支付宝交易失败: %w", err)
}

if resp.Code != "10000" {
    return fmt.Errorf(
        "支付宝查询失败: code=%s sub_code=%s sub_msg=%s",
        resp.Code,
        resp.SubCode,
        resp.SubMsg,
    )
}

switch resp.TradeStatus {
case "TRADE_SUCCESS", "TRADE_FINISHED":
    // 支付宝业务状态已确认成功
    markOrderPaid(resp.OutTradeNo, resp.TradeNo, resp.TotalAmount)

case "WAIT_BUYER_PAY":
    return fmt.Errorf("买家尚未完成支付")

case "TRADE_CLOSED":
    return fmt.Errorf("交易已经关闭")

default:
    return fmt.Errorf("未知交易状态: %s", resp.TradeStatus)
}

不要使用下面这种判断过于宽松的逻辑:

if resp.Code == "10000" {
    markOrderPaid()
}

接口查询成功不代表交易已经支付成功。

异步通知也要正确处理

如果商户系统依赖 notify_url 更新订单,需要确保:

  • 通知地址可以从公网访问。
  • 使用支付宝公钥或证书完成验签。
  • 校验通知中的 app_id、seller_id、out_trade_no 和金额。
  • 处理完成后,按接口要求返回成功响应。
  • 同一通知重复到达时,订单处理应具备幂等性。

示意代码:

if !verifyAlipaySignature(params) {
    return errors.New("支付宝通知验签失败")
}

if params["app_id"] != expectedAppID {
    return errors.New("app_id 不匹配")
}

if params["out_trade_no"] != expectedOutTradeNo {
    return errors.New("out_trade_no 不匹配")
}

if params["total_amount"] != expectedAmount {
    return errors.New("支付金额不匹配")
}

tradeStatus := params["trade_status"]
if tradeStatus == "TRADE_SUCCESS" || tradeStatus == "TRADE_FINISHED" {
    markOrderPaidIdempotently(params["out_trade_no"], params["trade_no"])
}

仍然卡住时怎么处理

如果已经确认以下条件全部满足:

  • 使用当前测试用例对应的沙箱 app_id
  • 使用沙箱网关和指定的沙箱买家
  • 使用全新的 out_trade_no
  • 支付发生在当前测试任务启动之后
  • alipay.trade.query 明确返回 TRADE_SUCCESS 或 TRADE_FINISHED
  • 页面正常完成跳转,异步通知也已通过验签并得到处理
  • 测试任务仍在剩余时间内

但测试步骤仍长期显示【等待用户完成支付】,更可能是测试平台状态同步延迟,或当前测试任务出现异常。此时可以停止当前任务并重新创建一次测试。如果问题能够稳定复现,再向平台技术支持提交:

  • 沙箱 app_id
  • 测试用例名称和执行时间
  • out_trade_no
  • trade_no
  • 支付接口原始响应
  • 查询接口原始响应
  • 异步通知日志及验签结果
  • 测试页面截图

提交材料时,可以隐藏私钥、签名原文和其他敏感凭据。不要只提供业务系统封装后的 {Status:1, Message:OK},因为这组信息不足以判断支付宝侧的交易状态,也无法说明测试平台为何没有完成关联。

备注:内容仅供参考。