# 公共套餐查询

更新：2026-10-28。公共接口实现已完成，正在等待后端发布与线上验收；文档可读不表示接口已经上线。旧版本可能返回接口不存在，客户端应显示可重试的加载状态，不展示内部错误。

## 用途与权限

所有客户端共用同一 SaaS 套餐数据源，不需要管理员的套餐管理权限；必须携带有效的平台登录 token。“公共”指跨业务共用，不是匿名访问。

此接口只展示套餐，不创建订单、不发放权益、不证明当前用户已订阅，也不授权购买。下架套餐可用于预览；归档套餐不返回。Apple 结算价格需以 StoreKit 商品信息为准。

## 请求

`GET https://work.zingyang.com/api/sys/saas/plan/catalog`

请求头：`X-Access-Token: <平台登录token>`。客户端仍可携带当前 `X-Tenant-Id`；目录是全局套餐定义，不返回任何租户的订阅信息。

| 参数 | 类型 | 说明 |
|---|---|---|
| planCodes | 字符串 | 必填，逗号分隔1–20个套餐编码；每个1–100字符，只允许英文字母、数字、下划线和连字符 |

```bash
curl --get 'https://work.zingyang.com/api/sys/saas/plan/catalog' \
  --header 'X-Access-Token: <平台登录token>' \
  --data-urlencode 'planCodes=cofree_plus_monthly,cofree_pro_monthly'
```

## 返回

以下为结构示例，不是线上实测响应：

```json
{
  "success": true,
  "code": 200,
  "message": "",
  "result": [
    {
      "id": "<后台套餐ID>",
      "name": "CoFree Plus 月付",
      "code": "cofree_plus_monthly",
      "billingCycle": "month",
      "price": 69,
      "currency": "CNY",
      "appleProductId": "com.zingyang.Cofree.plus.monthly"
    }
  ]
}
```

| 字段 | 说明 |
|---|---|
| id / code / name | 后台套餐ID、编码、名称 |
| billingCycle | month、year、trial、custom；不要把未知周期显示为月付 |
| price | 后台配置价格，可能为空；不是最终Apple结算报价 |
| currency | 配置币种，可能为空；缺失时不要猜测币种 |
| appleProductId | Apple商品ID，可能为空；存在也不表示商品已审核或可购买 |

结果按后台价格升序排列。编码不存在、归档或没有匹配项时返回空数组。接口不返回内部备注、Apple管理日志和密钥，也不返回尚未配置的额度或编造赠送权益。

## 错误和客户端行为

- 缺失/过期token：登录失败，按平台登录机制处理。
- 参数缺失或格式非法：请求错误（400），不要自动改成查询全部套餐。
- 网络或服务失败：显示友好的加载失败与重试按钮，不直接显示服务端路径或异常栈。
- 切换账号或组织时取消旧请求并清空旧状态，丢弃旧会话的迟到响应。
- 每次进入页面重新读取；失败不能悄悄回退到写死价格。

## 与已有接口的关系

`/sys/saas/plan/list` 仍是管理员管理接口，不供普通App获取套餐。
`/dashboard/analysis/current-plan` 查询当前组织已有订阅，不是待选择的套餐目录。
`/talk/v1/subscription/plans` 属于ZingTalk，包含Talk专属套餐和订阅逻辑，CoFree不应串用。
先前新增的 `/sys/saas/cofree/plans` 已从新版代码撤除，请使用本公共接口。
