新网站服务端 App 接口要求
# 新网站服务端 App 接口要求
更新日期:2026-09-15
本文提供给新网站后端、网页端和运维人员。目标是在不修改 App 业务协议的前提下,让新网站接入现有 App API 网关。网站后端需要实现或复用本文列出的上游接口;网关自有的分流、首页转换、App 更新和 Facebook CAPI 不要求网站重复实现。
## 1. 架构与责任边界
```text
App
-> 新站 App API 网关
-> 新网站业务 API:用户、套餐、余额、IAP、上传、创作记录、站点内容
-> 生成服务 API:提交任务、查询任务
-> D1:App 发版记录
-> Facebook CAPI:服务端事件
```
网站服务必须提供稳定的 HTTPS 地址,并向网关交付:
| 配置 | 必填 | 说明 |
|------|------|------|
| `HIGOON_PAYMENT_BASE_URL` | 是 | 新网站业务 API 根地址,不带末尾 `/` |
| `HIGOON_VIDEO_BASE_URL` | 是 | 新网站生成 API 根地址;与业务 API 相同时可填同一地址 |
| `HIGOON_SITE_ID` | 是 | 新网站站点 ID |
| `APP_ID` | 是 | 默认 `com.veloxreel.app`;新 App 应使用自己的 Bundle ID/应用标识 |
| iOS 商品配置 | 有内购时必填 | 数据库套餐 ID、App Store Product ID、Bundle ID 和 Apple 验证凭据 |
| App 发版信息 | 是 | platform、bundle ID、version、build、商店地址 |
| Meta Pixel ID | 使用广告事件时必填 | profile 返回当前站点有效 `pixelId` |
当前 Worker 配置是单套上游地址和单个默认站点。若新网站需要与现有网站同时独立运行,建议为新网站部署独立 Worker 或新增按域名选择配置的代码;不能只把新域名绑定到旧 Worker 而继续使用旧站 `SITE_ID`。
## 2. 通用协议要求
### 2.1 请求与站点隔离
- 所有业务接口使用 HTTPS。
- Header 名大小写不敏感,但实现必须正确读取 `Authorization`、`X-Site-Id`、`X-Locale`、`X-App-Version`、`X-App-Id` 和 `X-IOS-Proof`。
- `X-Site-Id` 必须参与数据隔离。Token 用户不属于该站点时应拒绝请求,不能跨站查询用户、余额、套餐或创作记录。
- JWT 必须能稳定解析出用户 ID;创作记录和余额接口不能相信客户端传入的 `userId`。
- 所有 JSON 请求和响应使用 UTF-8。
- 登录、登录码、资料、支付和验单响应必须设置 `Cache-Control: no-store`。
- 网站不能把密码、JWT、登录码、proof、Apple/Google 凭据或第三方密钥写入普通日志。
### 2.2 统一响应信封
成功:
```json
{
"code": 0,
"info": "ok",
"data": {}
}
```
失败:
```json
{
"code": 401,
"info": "Unauthorized",
"data": {}
}
```
要求:
- 成功同时使用 HTTP 2xx 和业务 `code=0`。
- 参数错误使用 HTTP 400,认证失败使用 401,权限/站点不匹配使用 403,限流使用 429,依赖不可用使用 503。
- `info` 用于展示或诊断,不作为唯一机器状态;同类错误应保持 `code` 稳定。
- 字段暂时无值时使用约定的空值或省略,不随意改变字段类型。
## 3. 网关与网站的路径映射
| App/网关路径 | 网站服务必须提供的路径 | 方法 | 鉴权 |
|--------------|------------------------|------|------|
| `/api/user/register` | `/api/user/register` | POST | 否 |
| `/api/user/login` | `/api/user/login` | POST | 否 |
| `/api/user/login/app-code` | `/api/user/login/app-code` | POST | 否 |
| `/api/user/app-login-code` | `/api/user/app-login-code` | POST | 是,网页端调用 |
| `/api/user/profile` | `/api/user/profile` | GET | 是 |
| `/api/user/change-password` | `/api/user/change-password` | POST | 是 |
| `/api/user/delete-account` | `/api/user/delete-account` | POST | 是 |
| `/api/payment/coins/package/list` | `/api/client/payment/coins/package/list` | GET | 否 |
| `/api/payment/subscription/package/list` | `/api/client/payment/subscription/package/list` | GET | 否 |
| `/api/payment/coins/balance` | `/api/client/payment/coins/balance` | GET | 是 |
| `/api/payment/iap/verify` | `/api/iap/verify` | POST | 是 |
| `/api/payment/iap/verify-subscription` | `/api/iap/verifysub` | POST | 是 |
| `/api/client/upload` | `/api/client/upload` | POST | 否 |
| `/api/client/creations` | `/api/client/creations` | GET | 是 |
| `/api/client/creations/{taskId}` | `/api/client/creations/{taskId}` | DELETE | 是 |
| `/api/plugins/video/generate` | `/api/plugins/video/generate` | POST | 是 |
| `/api/plugins/video/query` | `/api/plugins/video/query` | GET | 是 |
`POST /api/home/template/generate` 是网关自有接口,不要求网站新增同名路径。网关会校验 Home `item`,选择受支持模型并转换为现有 `POST /api/plugins/video/generate` 请求。
另外,网关生成自然量首页需要网站提供:
```http
GET /api/client/site/plist?site_id=<SITE_ID>&orderType=0&page=1&pageSize=80
```
## 4. 用户接口
### 4.1 POST /api/user/register
Header:`X-Site-Id` 必填,`X-App-Version` 可选。
```json
{
"email": "user@example.com",
"password": "12345678",
"nickname": "user"
}
```
| 字段 | 类型 | 必填 | 服务端校验 |
|------|------|------|------------|
| `email` | string | 是 | 合法邮箱;按产品规则归一化和唯一校验 |
| `password` | string | 是 | 至少 6 个字符,安全散列保存 |
| `nickname` | string | 否 | 做长度和安全字符限制 |
成功 `data`:
```json
{
"accessToken": "<jwt>",
"siteId": "<SITE_ID>",
"ver": "1.0.0"
}
```
### 4.2 POST /api/user/login
Header:`X-Site-Id` 必填。
请求:
```json
{
"email": "user@example.com",
"password": "12345678"
}
```
成功 `data` 至少返回 `accessToken` 和 `ver`。Token claims 必须包含后续中间件可识别的用户 ID,并有明确有效期。
### 4.3 GET /api/user/profile
Header:`Authorization: Bearer <token>`、`X-Site-Id` 必填。
成功 `data`:
```json
{
"id": "user-id",
"email": "user@example.com",
"nickname": "user",
"avatarUrl": "",
"premiumType": 0,
"onetimeSub": 0,
"premiumExpiresAt": 0,
"autoUnlock": false,
"status": 1,
"referer": "",
"pixelId": "1234567890",
"loginType": 0,
"hasPurchased": false,
"ver": "1.0.0"
}
```
`pixelId` 被网关 Facebook 事件接口使用。启用 CAPI 时,必须返回新站自己的 Pixel ID,不能复用其他站点值。
### 4.4 POST /api/user/change-password
Header:`Authorization`、`X-Site-Id` 必填。
```json
{
"newPassword": "new-password"
}
```
`newPassword` 必填,长度 6~64。成功 `data={}`。修改后是否撤销旧 Token 必须形成明确策略并告知 App。
### 4.5 POST /api/user/delete-account
Header:`Authorization`、`X-Site-Id` 必填。无请求参数。根据认证用户执行软删除或合规删除,成功 `data={}`;不得接受 body 中的其他用户 ID。
## 5. 网页到 App 的六位码登录
该能力包含网页端签发和 App 端兑换两个接口。新网站网页必须增加“在 App 登录”入口,只有已登录用户可以生成登录码。
### 5.1 POST /api/user/app-login-code
调用方:已登录网页端。
Header:
```http
Authorization: Bearer <web-user-token>
X-Site-Id: <SITE_ID>
Content-Type: application/json
```
请求体:
```json
{
"appId": "com.example.app"
}
```
`appId` 必填,最长 128 字符,服务端归一化后必须命中允许的 App。用户 ID 只从 Token 获取。
成功:
```json
{
"code": 0,
"info": "ok",
"data": {
"code": "003821",
"expiresIn": 300
}
}
```
网页必须把 `data.code` 当字符串显示,保留前导零,并显示服务端返回的倒计时。登录码属于敏感凭据,不得进入分析平台。
### 5.2 POST /api/user/login/app-code
调用方:App。
```json
{
"loginCode": "003821"
}
```
`loginCode` 必须是六位 ASCII 数字字符串。单 App 默认模式可以省略 `X-App-Id`;多 App 场景必须验证 `X-App-Id`。严格模式必须验证一次性 `X-IOS-Proof` 与请求方法、路径、body、App ID 和有效期匹配。
成功 `data`:
```json
{
"accessToken": "<new-jwt>",
"ver": "",
"iosLoginReward": {
"granted": true,
"amount": 500,
"balance": 500
}
}
```
服务端验收要求:
- 登录码默认 300 秒有效,最多成功消费一次。
- 同一用户和 App 重新签发时,旧码立即失效。
- 码签发、消费、首次登录标记、奖励入账和流水具备事务一致性。
- 首次奖励按用户只发一次;重复登录不能重复领取。
- 登录码、proof 和 IP/安装实例必须有限流。
- 多副本部署时,状态、限流和防重放数据必须共享,不能只存在进程内存。
- 错误码至少覆盖 400、401、403、429、503。
当前 `shortpress-server` 已实现这两个路由及数据库/Redis 降级逻辑。复用该项目时,应完成迁移并将默认 App ID 改为新 App,而不是继续沿用旧 Bundle ID。
## 6. 套餐与余额
### 6.1 GET /api/client/payment/coins/package/list
站点可同时来自 `X-Site-Id`、`siteId` 和 `site_id`。三者同时出现时必须一致;冲突时返回 400 或 403,不能随机选择。
只返回当前站点启用的套餐。`data[]`:
| 字段 | 类型 | 必填 |
|------|------|------|
| `packageId` | string | 是 |
| `name` | string | 是 |
| `description` | string | 否 |
| `features` | object/array | 否 |
| `coinAmount` | integer | 是 |
| `price` | number/string | 是 |
| `originalPrice` | number/string | 否 |
| `currency` | string | 是 |
| `discountPercentage` | integer | 否 |
| `status` | integer | 是 |
| `iosProductId` | string | iOS 必填 |
### 6.2 GET /api/client/payment/subscription/package/list
只返回当前站点启用的订阅套餐。`data[]` 至少包含:`packageId`、`siteId`、`name`、`description`、`interval`、`price`、`originalPrice`、`currency`、`discountPercentage`、`coins`、`rights`、`status`、`iosProductId`、`createdAt`。
### 6.3 GET /api/client/payment/coins/balance
Header:`Authorization`、`X-Site-Id` 必填。用户 ID 从 JWT 获取。
成功 `data`:
```json
{
"balance": 500,
"totalEarned": 500,
"totalSpent": 0,
"totalRealMoneySpent": 0
}
```
余额读取、生成扣币、失败退款、购买入账和登录奖励必须使用同一套账本口径。
## 7. IAP 验单
### 7.1 POST /api/iap/verify
用途:一次性金币商品验单和发货。
Header:`Authorization`、`X-Site-Id`、`Content-Type: application/json`。
```json
{
"transactionId": "2000000123456789",
"account": "apple",
"packageName": "com.example.app",
"packageId": "server-package-id",
"productId": "app-store-product-id",
"purchaseToken": ""
}
```
服务端必须校验交易真实性、Bundle ID、商品 ID、站点套餐关系、购买状态和重复交易。`packageId` 是网站数据库 ID,`productId` 是商店商品 ID,不能混用。交易 ID 必须具备数据库唯一约束或等效幂等机制。
成功:
```json
{
"code": 0,
"info": "ok",
"data": {
"productId": "app-store-product-id"
}
}
```
### 7.2 POST /api/iap/verifysub
请求字段同金币验单。成功 `data`:`isActive`、`sandbox`、`isInFreeTrial`、`autoRenewStatus`。
订阅有效期、续订和退款状态以商店服务端数据为准。沙盒和生产交易必须明确区分。网关在上游验单成功后可能异步上报 Facebook Purchase,因此网站返回成功前必须确保交易已经持久化。
## 8. 文件上传
### POST /api/client/upload
请求为 `multipart/form-data`,字段 `file` 恰好一个。当前兼容协议允许无 Token,但必须实施文件安全限制:
- 限制请求体和单文件大小。
- 校验实际 MIME 和文件签名,不只看扩展名。
- 生成不可预测文件名,禁止目录穿越和覆盖已有文件。
- 存储桶默认不可执行上传内容。
- 返回的路径必须可被生成服务访问。
- 对匿名上传实施 IP、设备或站点级限流,并配置生命周期清理。
成功:
```json
{
"code": 0,
"info": "ok",
"data": "res/user_upload/directory/file.jpg"
}
```
上线前必须与 App/网关约定 `data` 是绝对 URL 还是相对路径。如果返回相对路径,应提供唯一且稳定的 CDN 基地址。
## 9. 生成服务
生成服务可与网站业务 API 同域,也可独立部署。无论部署方式,网关必须能通过 `HIGOON_VIDEO_BASE_URL` 访问以下两个路径。
### 9.1 POST /api/plugins/video/generate
Header:`Authorization`、`X-Site-Id` 必填。生成服务必须验证真实 JWT 用户身份和站点权限,不能只检查 Header 非空。
```json
{
"model": "viduq2_image",
"video_id": "template-1",
"number": 1,
"args": {
"image": "https://cdn.example.com/input.jpg",
"prompt": "跳舞",
"duration": "5",
"resolution": "720p"
}
}
```
处理要求:
- 同一接口也会接收网关由 `/api/home/template/generate` 转换出的 `aitubo_dance`、`aitubo_template`、`sora_template` 和 `mulerouter/w3.0-video` 请求;网站服务应保持这些模型及其现有参数协议可用。
- `model` 必须来自服务端允许清单,拒绝未知模型。
- `args` 按模型 schema 校验必填字段、枚举、URL、数量、时长和分辨率。
- 服务端根据模型和 `number` 计算价格,不能相信客户端提交价格。
- 扣币和任务创建需具备一致性;第三方失败后按实际成功数量退款。
- 任务记录必须写入共享存储,并包含 `taskId`、`siteId`、`userId`、模型、状态和时间。
- 输入 URL 需防 SSRF,只允许可信对象存储/CDN 或经过下载代理校验。
成功 `data`:`task_id` 和 `model`。
### 9.2 GET /api/plugins/video/query
Query:`task_id` 必填,`model` 可选。Header:`Authorization`、`X-Site-Id` 必填。
必须验证任务属于 JWT 用户和当前站点。不能只凭 `task_id` 返回其他用户结果。
`data.status`:`1` 处理中、`2` 成功、`3` 失败。结果字段兼容:
```json
{
"task_id": "task-id",
"status": 2,
"model": "viduq2_image",
"prompt": "跳舞",
"videos": [
{
"url": "https://cdn.example.com/result.mp4",
"cover_url": "https://cdn.example.com/cover.jpg"
}
],
"images": [],
"error_msg": "",
"created_at": "2026-09-15T00:00:00Z",
"updated_at": "2026-09-15T00:01:00Z"
}
```
## 10. 当前用户创作记录
### GET /api/client/creations
Header:`Authorization`、`X-Site-Id` 必填。
| Query | 类型 | 默认 | 限制 |
|-------|------|------|------|
| `page` | integer | `1` | 小于 1 时按 1 |
| `pageSize` | integer | `20` | 1~100,非法值按 20 |
用户 ID 只能从 JWT 获取。当前兼容范围是最近 24 小时的生成记录。
成功:
```json
{
"code": 0,
"info": "ok",
"data": {
"items": [
{
"taskId": "task-id",
"status": 2,
"model": "viduq2_image",
"videoId": "template-1",
"siteId": "site-id",
"userId": "user-id",
"prompt": "跳舞",
"referenceImages": ["https://cdn.example.com/input.jpg"],
"videos": [
{
"url": "https://cdn.example.com/result.mp4",
"coverUrl": "https://cdn.example.com/cover.jpg"
}
],
"images": [],
"errorMsg": "",
"createdAt": 1789430400,
"updatedAt": 1789430460
}
],
"total": 1,
"page": 1,
"pageSize": 20,
"hasMore": false
}
}
```
注意查询接口的 `coverUrl` 使用驼峰,而生成查询接口当前兼容字段是 `cover_url`。如需统一,必须同时升级网关和 App,不能单方面改字段。
### DELETE /api/client/creations/{taskId}
Header:`Authorization`、`X-Site-Id` 必填。服务端必须从 JWT 获取用户 ID,并校验 `taskId` 同时属于该用户和当前站点;不得接受客户端提交的 `userId`。
仅允许删除状态为 `2`(成功)或 `3`(失败)的已结束任务。状态 `1`(处理中)必须返回 HTTP 409;不存在或所有权不匹配返回 HTTP 404。删除应为用户可见层面的软删除,生成任务、扣费、退款和运营审计记录必须保留。
成功:
```json
{ "code": 0, "info": "ok", "data": {} }
```
错误响应必须保持稳定:
```json
{ "code": 409, "info": "只能删除已结束任务", "data": {} }
```
```json
{ "code": 404, "info": "创作记录不存在", "data": {} }
```
实现要求:
- 删除条件必须以服务端读取到的任务状态为准,不能相信客户端提交的状态。
- 所有权不匹配与任务不存在统一返回 404,避免泄露其他用户任务是否存在。
- 重复删除同一条已隐藏但仍保留快照的记录应保持幂等,不得重新扣费、退款或修改任务状态。
- 删除成功后,后续 `GET /api/client/creations` 不再返回该 `taskId`,并同步修正 `total`。
## 11. 自然量首页数据
### GET /api/client/site/plist
网关请求:
```http
GET /api/client/site/plist?site_id=<SITE_ID>&orderType=0&page=1&pageSize=80
Accept: application/json
```
无需用户 Token,但必须按 `site_id` 返回当前站点已发布、可公开展示的列表,排除不应出现在自然量 App 的内容。
成功 `data`:
```json
{
"total": 1,
"page": 1,
"pageSize": 80,
"hasMore": false,
"items": [
{
"playlistId": "playlist-id",
"title": "Example",
"slug": "example",
"description": "",
"tags": "",
"cover": "https://cdn.example.com/cover.jpg",
"status": 1,
"videoCount": 1,
"accessType": 0,
"singleVideoPrice": 0,
"videos": [
{
"vid": "video-id",
"status": 2,
"unlockStatus": 1,
"cover": "https://cdn.example.com/cover.jpg",
"local_path": "https://cdn.example.com/video.mp4",
"config": {
"coin": 0,
"effectId": "",
"templateType": ""
}
}
]
}
]
}
```
网关会把该结构转换成首页统一 Feed,并随机返回部分条目。`cover` 和 `local_path` 必须是 App 可访问的 HTTPS 地址。
## 12. 网关自有能力的网站配合项
以下接口不由网站直接实现,但网站开发和运维需要提供数据或配置:
| 网关接口 | 网站/运维配合 |
|----------|---------------|
| `/api/create-page/config` | 提供新站模型、价格、参数、默认 Tab 和流量版本的配置 JSON |
| `/api/home/*` | 提供 `/api/client/site/plist` 内容;确认广告流量 Feed 来源是否继续复用现有服务 |
| `/api/app/update/check` | 为新 App 的平台和 Bundle ID 写入 published 发版记录 |
| `/api/ads/facebook/events` | profile 返回新站 `pixelId`,运维在网关安全配置 CAPI Token |
| IAP 成功后的 Purchase | 确保套餐、币种、金额和交易 ID 可供网关正确上报和去重 |
## 13. 网页端开发要求
网页端至少增加以下功能:
1. 已登录用户可点击“在 App 登录”。
2. 网页调用 `POST /api/user/app-login-code`,携带当前用户 Token、新站 `X-Site-Id` 和新 App `appId`。
3. 展示六位字符串和 `expiresIn` 倒计时,保留前导零。
4. 用户重新生成后立即替换旧码,不继续展示多个码。
5. 401 时引导网页重新登录;429 时显示稍后再试;503 时显示服务暂不可用。
6. 不在 URL、HTML 源码、埋点、日志或客服截图中长期保存登录码。
网页不需要实现 App 兑换请求,也不能获取 `X-IOS-Proof` 的签名私钥。
## 14. 部署前交付清单
网站团队应一次性交付:
- 新网站业务 API 根地址和生成 API 根地址。
- 生产、预发布各自的 `SITE_ID`,不得混用。
- 新 App 的 `APP_ID`、iOS Bundle ID、Android 包名。
- 所有上游接口的可执行 curl/Postman 集合,凭据通过安全渠道提供。
- 测试账号、测试套餐、Apple 沙盒账号和可安全使用的测试图片。
- 上传文件的公开 URL 拼接规则、大小上限和生命周期。
- 视频模型清单及每个模型的 `args` JSON Schema、价格和退款规则。
- App 发版记录和商店地址。
- Meta Pixel ID 与 CAPI 环境归属。
- 数据库迁移、回滚、监控和报警方案。
## 15. 联调验收用例
| 编号 | 用例 | 预期结果 |
|------|------|----------|
| `AUTH-01` | 新站注册、登录、profile | Token 仅能访问新站用户 |
| `AUTH-02` | 用新站 Token 请求旧站 ID | 返回 401/403,不泄露数据 |
| `CODE-01` | 网页生成六位码并由 App 兑换 | 首次成功,返回原账号 Token |
| `CODE-02` | 重复兑换同一码 | 第二次返回 401 |
| `CODE-03` | 过期、错误 App 或错误站点兑换 | 返回 401/403 |
| `PAY-01` | 获取金币和订阅套餐 | 只出现新站启用套餐 |
| `IAP-01` | 沙盒金币交易验单两次 | 只发货一次 |
| `IAP-02` | 沙盒订阅验单 | 状态和有效期正确 |
| `UPLOAD-01` | 上传合法图片 | 返回生成服务可访问地址 |
| `UPLOAD-02` | 上传超限或伪造类型文件 | 明确拒绝,不落入公开可执行目录 |
| `GEN-01` | 图生视频完整流程 | generate、query、creations 数据一致 |
| `GEN-02` | 查询其他用户 task ID | 返回 401/403/404,不返回结果 |
| `COIN-01` | 生成成功、失败和部分成功 | 扣币及退款账本正确 |
| `HOME-01` | 获取自然量首页 | 内容只来自新站且媒体可播放 |
| `UPDATE-01` | 新旧版本检查 | 正确返回更新状态和商店 URL |
| `ADS-01` | 测试环境上报 Lead/Purchase | 进入新站 Pixel,eventId 可去重 |
全部用例通过后,再把新站域名、上游地址和站点 ID 切换到生产配置。