PAYATHON 2026

未启用付费 REST API 功能时应返回什么 HTTP 状态码?

支付老李

结论

用户已通过身份认证,但当前账号没有启用相应的付费功能,建议返回 403 Forbidden。

403 表示服务器理解该请求,也知道请求者是谁,但不允许其访问当前资源或执行相应操作。未购买套餐、功能尚未启用、缺少产品授权,都属于权限不足或 entitlement 条件未满足。

通常不建议使用 402 Payment Required。这个状态码长期处于保留状态,通用语义、客户端处理方式和生态支持都不明确。只有当 API 将 402 明确定义为计费协议的一部分,并且所有调用方都了解这一约定时,才适合使用。其他情况下,403 更合适。

不同情况应该如何选择

已登录,但没有购买或启用功能:403 Forbidden

这正是问题中的主要场景:

HTTP/1.1 403 Forbidden
Content-Type: application/problem+json

{
  "type": "https://api.example.com/problems/feature-not-enabled",
  "title": "Feature not enabled",
  "status": 403,
  "detail": "This endpoint requires the analytics_pro feature.",
  "code": "FEATURE_NOT_ENABLED",
  "required_feature": "analytics_pro",
  "activation_url": "https://example.com/settings/billing"
}

HTTP 状态码负责说明请求遭到拒绝,具体原因则应通过稳定的业务错误码表达。客户端可以根据 FEATURE_NOT_ENABLED 显示升级入口,无需解析自然语言。

尚未认证:401 Unauthorized

如果服务器无法确认调用者身份,例如 token 缺失、无效或已经过期,应返回 401:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
Content-Type: application/problem+json

{
  "title": "Authentication required",
  "status": 401,
  "code": "INVALID_ACCESS_TOKEN"
}

虽然状态码名称中有 “Unauthorized”,但 401 实际表示认证失败。调用者已经通过认证,只是权限不足时,才使用 403。

为避免泄露资源存在性:可考虑 404 Not Found

如果需要向无权限用户隐藏付费 endpoint 或资源是否存在,可以返回 404。这是一种安全策略,并非常规的”未购买”提示。

例如,多租户系统可能不希望调用者根据响应差异判断某项资源是否存在:

HTTP/1.1 404 Not Found
Content-Type: application/problem+json

{
  "title": "Resource not found",
  "status": 404,
  "code": "RESOURCE_NOT_FOUND"
}

如果付费 API 已经写在公开文档中,返回 404 通常没有明显好处,还可能让调用者误以为 URL 写错了。

推荐的处理流程

服务端可以依次进行以下判断:

  1. 检查凭证是否存在且有效,失败时返回 401。
  2. 检查账号是否有权访问目标资源,失败时返回 403;如果需要隐藏资源,则返回 404。
  3. 检查账号是否具备 endpoint 要求的付费功能,未启用时返回 403,并附上业务错误码。
  4. 检查请求参数和业务状态,根据具体问题返回相应的 4xx 错误。
  5. 权限检查通过后,执行请求并返回正常结果。

Express 示例:

app.get(
  "/v1/advanced-analytics",
  authenticate,
  async (req, res) => {
    const enabled = await hasFeature(
      req.user.accountId,
      "analytics_pro"
    );

    if (!enabled) {
      return res.status(403).type("application/problem+json").json({
        type: "https://api.example.com/problems/feature-not-enabled",
        title: "Feature not enabled",
        status: 403,
        detail: "This endpoint requires the analytics_pro feature.",
        code: "FEATURE_NOT_ENABLED",
        required_feature: "analytics_pro",
        activation_url: "https://example.com/settings/billing"
      });
    }

    const result = await generateAdvancedAnalytics(req.user.accountId);
    return res.status(200).json(result);
  }
);

为什么其他状态码不合适

  • 402 Payment Required:可以用于私有 API 约定,但没有统一、可互操作的标准语义,不能只因为名称看起来合适就使用。
  • 404 Not Found:适合隐藏资源,不适合普通的套餐权限提示。
  • 409 Conflict:表示请求与资源当前状态发生冲突,不表示访问授权失败。
  • 422 Unprocessable Content:表示请求内容语法正确,但服务器无法处理,不适用于套餐权限判断。
  • 429 Too Many Requests:用于限流或配额消耗过快,不能表示功能尚未购买。

如果用户已经购买该功能,只是当月额度耗尽,可以根据产品规则返回 429 Too Many Requests,再通过响应体或相关 header 说明配额及恢复时间。这与功能从未启用是两种不同的情况。

注意事项

错误响应应当稳定,并且可由程序识别。不要只返回 "Please upgrade" 这样的字符串,最好同时包含:

  • HTTP 状态码 403
  • 稳定的业务错误码,例如 FEATURE_NOT_ENABLED
  • 所需功能的标识
  • 面向用户的说明
  • 可选的启用或升级地址

错误信息不应泄露内部价格、账单状态或账号权限细节,除非调用者确实有权查看这些信息。API 文档也应明确说明付费 endpoint 可能返回 403,并列出对应的业务错误码。

默认规则可以概括为:未认证返回 401;已认证但未启用付费功能返回 403;需要隐藏资源是否存在时,再考虑返回 404。

备注:内容仅供参考。