PAYATHON 2026

沙箱支付过期后未收到 notify,是否需要配置其他接口?

支付阿杰

明确结论

沙箱订单过期后没有收到 notify,不一定是配置出了问题。许多支付系统只会在支付成功、退款等状态发生变化时发送异步通知,订单超时自动失效时未必会通知。沙箱和正式环境的通知机制也可能存在差异。

一般不需要在 pay 接口中额外设置“过期通知”参数。即使接口支持 notify_url,这个字段通常也只是用来指定支付结果的通知地址,并不表示所有订单状态变化都会触发回调。

是否支持过期通知,要以当前支付产品及接口版本的官方文档为准。

为什么收不到过期通知

可能的原因包括:

  • 支付平台没有将“订单过期”定义为通知事件。
  • notify_url 只用于通知支付成功或交易结果。
  • 沙箱环境仅模拟了部分通知场景。
  • 订单已经超过商户设置的有效期,但平台侧尚未将其关闭。
  • 通知地址无法从公网访问,或者 HTTPS、证书、响应格式不符合平台要求。
  • 平台已经发送通知,但商户接口没有及时返回规定的成功响应。平台重试后,业务系统仍未正确记录通知。

还要注意,前端支付页面显示已过期,不代表支付平台会在同一时刻把订单状态改为关闭。

建议的处理方式

1. 确认 notify_url 的用途

检查 pay 请求是否要求传入类似字段:

{
  "notify_url": "https://example.com/payment/notify"
}

实际字段名可能是 notify_url、notifyUrl 或其他形式,具体以 API 文档为准。不要添加接口没有定义的参数。

还需要确认文档是否明确说明:

  • 哪些交易状态会发送通知;
  • 订单超时或关闭是否属于通知事件;
  • 沙箱环境是否支持该事件;
  • 通知失败后如何重试;
  • 接收方需要返回什么响应。

2. 主动查询订单状态

如果平台不保证发送过期通知,商户系统应通过订单查询接口确认状态,不能只依赖异步回调更新订单。

下面是一个通用示例。接口地址和字段仅用于展示结构,使用时需要替换为支付平台的实际定义:

async function queryPaymentStatus(orderId) {
  const response = await fetch(
    `https://sandbox.example.com/api/payments/${encodeURIComponent(orderId)}`,
    {
      method: "GET",
      headers: {
        Authorization: `Bearer ${process.env.PAYMENT_API_KEY}`,
        Accept: "application/json"
      }
    }
  );

  if (!response.ok) {
    throw new Error(`Query payment failed: ${response.status}`);
  }

  const payment = await response.json();

  if (payment.status === "PAID") {
    await markOrderAsPaid(orderId);
  } else if (
    payment.status === "EXPIRED" ||
    payment.status === "CLOSED" ||
    payment.status === "CANCELLED"
  ) {
    await markOrderAsClosed(orderId);
  }

  return payment;
}

PAID、EXPIRED、CLOSED 和 CANCELLED 是常见的状态名称,目标平台不一定使用这些值。

3. 在本地设置超时检查任务

创建支付订单时,可以保存平台订单号和预计过期时间。定时扫描已经超过有效期、状态仍为“待支付”的订单,再调用支付查询接口核实状态。

SELECT id, payment_order_id
FROM orders
WHERE status = 'PENDING'
  AND expires_at <= CURRENT_TIMESTAMP;

订单最终状态应以支付平台的查询结果为准。不要因为本地时间已经到期,就直接把订单认定为付款失败。

4. 必要时调用关单接口

在部分平台中,“过期时间”只是限制用户继续支付,并不会自动把订单状态改为关闭。这种情况下,可能需要主动调用关单接口:

async function closeExpiredPayment(paymentOrderId) {
  const response = await fetch(
    `https://sandbox.example.com/api/payments/${encodeURIComponent(paymentOrderId)}/close`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.PAYMENT_API_KEY}`,
        "Content-Type": "application/json"
      }
    }
  );

  if (!response.ok) {
    throw new Error(`Close payment failed: ${response.status}`);
  }

  return response.json();
}

平台是否允许主动关单、关单后是否发送通知,以及对已支付订单调用关单接口时会如何处理,都要按照实际接口规则判断。

回调接口还需要检查什么

即使订单过期不会触发通知,也要检查现有回调配置是否正常:

  • notify_url 必须能被支付平台从公网访问。
  • 不要使用 localhost、内网 IP 或已经失效的临时测试地址。
  • 检查请求签名和平台证书的验证逻辑。
  • 保存原始通知内容及请求头,方便排查问题。
  • 回调处理需要具备幂等性,防止重复通知造成重复入账。
  • 收到通知后,按平台要求返回成功响应。
  • 对支付成功等重要状态,最好再调用查询接口确认。

推荐的订单状态同步策略

更稳妥的做法是配合使用以下三种机制:

  1. 通过异步通知及时更新支付结果。
  2. 通过主动查询补偿通知丢失或未覆盖过期状态的情况。
  3. 通过定时任务处理长期停留在待支付状态的订单。

因此,沙箱订单过期后没有收到 notify,可以先按可能的正常行为排查。首先确认官方文档是否明确支持“过期/关闭通知”。如果文档没有作出保证,就使用订单查询接口和定时任务处理,不要继续猜测或在 pay 接口中添加未经定义的参数。

备注:内容仅供参考。