Files
store_ai_front/BACKEND_API_DOCUMENTATION.md
2026-05-11 10:05:33 +08:00

96 KiB
Raw Blame History

Backend API 文档

生成日期2026-05-08 生成依据:backend/routes/api.phpphp artisan route:list --path=apiapp/Http/Controllers/Api/**app/Services/**

本文档是当前 backend API 的唯一维护入口。backend/docs 下的旧 API 文档已移除,后续接口变更统一更新本文件。

本文档只覆盖当前 backend 暴露的 API 路由,共 43 个路由入口。Laravel 自动支持的 HEAD 不单独列出。

通用约定

Base URL

生产或本地部署时以实际后端域名为准,下文统一写作:

{BACKEND_API_BASE_URL}/api

本地默认通常是:

http://127.0.0.1:8000/api

请求头

请求头 适用接口 说明
Content-Type: application/json 有 JSON body 的接口 请求体使用 JSON。
Authorization: Bearer <sanctum_token> Dashboard / 当前用户接口 Laravel Sanctum Web 用户 token。邮箱已验证账号可由 /api/auth/login 返回;邮箱未验证账号必须先通过 /api/auth/email/verify-code,验证码校验成功后才返回 token重置密码仅在账号邮箱已验证时返回 token。Dashboard 路由会拒绝扩展 token。
Authorization: Bearer <extension_sanctum_token> Chrome 扩展接口 扩展专用 Sanctum token/api/auth/extension-pair/api/auth/refresh-extension-token 返回;后端会校验 storeai_extension token name 和 extension:* ability。扩展 token 只能访问扩展接口和 /api/auth/logout
X-Ext-Version: <version> 扩展扫描相关接口 用于扩展版本门禁,低于 MIN_EXT_VERSION 会返回 ext_outdated
Authorization: Bearer <CRON_SECRET> Cron 接口 配置 CRON_SECRET 后必填;生产环境未配置会返回 misconfigured
Stripe-Signature: <signature> Stripe webhook Stripe webhook 原始 body 签名。
X-Telegram-Bot-Api-Secret-Token: <secret> Telegram webhook Telegram webhook secret token。

响应格式

所有 backend API 统一返回三元素 envelope。前端已确定重写不再保留旧 { ok: ... } 顶层兼容格式。

{
  "code": 1,
  "message": "ok",
  "data": {}
}

失败响应的顶层 code 放整型 status旧接口字符串错误码统一放入 data.apiCode;异常响应不改变实际 HTTP status codeHTTP 层保持 200

{
  "code": 400,
  "message": "Validation failed.",
  "data": {
    "apiCode": "invalid_payload"
  }
}

部分错误会附带额外字段:

{
  "code": 400,
  "message": "schema validation failed",
  "data": {
    "apiCode": "invalid_payload",
    "details": {
      "fieldErrors": {
        "payload.today.orders": ["must be an integer"]
      }
    }
  }
}

空业务数据返回空数组或空对象,取决于控制器业务返回值;客户端只应依赖外层 code/message/data

{
  "code": 1,
  "message": "ok",
  "data": {}
}

通用错误

下表的 响应 code 指 JSON 响应体里的 code 字段,不是 HTTP status code异常响应的 HTTP status 保持 200。

响应 code data.apiCode 说明
400 invalid_payload / invalid_email / invalid_code 请求参数格式错误。
401 unauthorized 缺少 token、token 无效、签名错误或未登录。
402 subscription_required 当前订阅状态不允许扫描。
404 not_found 资源不存在、无权访问或 debug 门禁关闭。
409 platform_account_mismatch / subscription_active / agreement_stale 业务状态冲突。
426 ext_outdated Chrome 扩展版本低于后端要求。
429 too_many_requests / rate_limited 请求过于频繁或手动扫描达到日限额。
500 server_error / config_error / stripe_error / handler_error 服务端错误、配置缺失或第三方调用失败。
503 misconfigured 生产环境缺少必需配置。

公共对象

User

字段 类型 说明
id string Laravel 用户 UUID。
email string 邮箱。
name string|null 用户名,可为空。
emailVerifiedAt string|null 邮箱验证时间ISO 字符串。

ApiUser

字段 类型 说明
id string 当前登录用户 UUID。
email string|null 当前登录用户邮箱。

BrandCore

BrandCore 是 Dashboard 读接口返回的品牌基础对象。不同接口会按页面需要裁剪或追加字段。

字段 类型 说明
id string 品牌 UUID。
name string 品牌名称。
timezone string|null 品牌时区,例如 Asia/Kuala_Lumpur
morning_brief_hour integer|null 早报本地小时0-23。
evening_recap_hour integer|null 晚报本地小时0-23。
last_morning_brief_at string|null 最近早报发送时间。
last_evening_recap_at string|null 最近晚报发送时间。
subscription_status string|null 订阅状态,例如 trialactivepast_duecanceled
trial_ends_at string|null 试用结束时间。
subscription_cancel_at string|null 订阅预约取消时间。
is_comp boolean 是否内部赠送账号。
telegram_chat_id string|null Telegram chat id。
platform_account_id string|null 已绑定 Shopee seller id。
created_at string|null 创建时间。
last_extension_version string|null 最近扩展心跳版本。
stores array 店铺列表,元素为 { id: string }

subscription_status 枚举

subscription_statusbrands 表保存的原始账单状态;前端判断“是否可用 / 是否到期”时还要结合 is_comptrial_ends_atsubscription_cancel_at

含义 到期时间口径
trial 试用中。创建首个品牌时后端写入 trial_ends_at = now + 1 day 使用 trial_ends_at 判断试用到期;为空时按试用可用但无倒计时时间处理。
active Stripe 订阅有效。 subscription_cancel_at 为空表示当前无已知到期时间;若为未来时间,表示已预约取消,付费权益到 subscription_cancel_at
past_due Stripe 付款异常或 unpaid 当前 API 不给确定到期时间Stripe 后续重试失败并发送 deleted webhook 后会变为 canceled
canceled 订阅已取消或 Stripe incomplete_expired 已到期,不再使用未来到期时间;后端会清空 subscription_cancel_at
null / 其它值 旧数据或异常数据。 按无有效订阅处理。

有效订阅状态的派生规则:

条件 派生状态 扫描权限
is_comp = true comp 允许,内部赠送账号优先级最高。
subscription_status = trialtrial_ends_at 为空或晚于当前 UTC 时间 trial_active 允许。
subscription_status = trialtrial_ends_at 已过期或无法解析 trial_expired 不允许。
subscription_status = activesubscription_cancel_at 为空 active 允许。
subscription_status = activesubscription_cancel_at 晚于当前 UTC 时间 active_canceling 允许,展示“已预约取消”。
subscription_status = activesubscription_cancel_at 已过期或无法解析 canceled 不允许。
subscription_status = past_due past_due 允许,但应提示用户更新付款方式。
subscription_status = canceled canceled 不允许。

如果前端要展示“订阅到期时间”,优先按以下顺序取值:is_comp=true 时无到期时间;trialtrial_ends_atactive 且已预约取消时取 subscription_cancel_at;普通 active 当前没有到期时间字段,因为后端暂未保存 Stripe current_period_end

ScanRow

字段 类型 说明
id string scan UUID。
scanned_at string 扫描时间。
status string okpartialfailed
error string|null 扩展提取错误文本。
payload object|null 扫描原始 payload。
telegram_message_id integer|null 手动扫描 Telegram 回声消息 id。

AlertRow

字段 类型 说明
id string alert UUID。
rule_id string 规则 ID。
priority string 告警优先级,例如 P1P2
confidence string 置信度。
title string 标题。
diagnosis string 诊断文案。
action string 建议动作。
data object 告警附加 JSON。
acknowledged boolean 是否已确认。
created_at string 创建时间。

StoreBrief

字段 类型 说明
verdict_tier string|null AI 今日结论等级。
verdict_headline string|null AI 今日结论标题。
verdict_factors array AI 结论因素。
verdict_action string|null AI 建议行动。
bottleneck_stage string|null 增长瓶颈阶段。
growth_diagnosis_json object|null AI 增长诊断 JSON。
top_actions_json array|null AI Top Actions。
confidence object AI 置信度 JSON。

系统接口

GET /api/health

鉴权:无。

入参:无。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/health"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.service string 固定为 storeai-api
data.version string|null config('app.version')
data.environment string 当前 Laravel 环境。

认证接口

POST /api/auth/register

鉴权:无。

入参:

位置 字段 类型 必填 说明
body email string 合法邮箱,服务端会转小写并 trim。
body password string 8-128 字符。
body name string|null 最多 120 字符,空字符串按 null。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/auth/register" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "owner@example.test",
    "password": "secure-password",
    "name": "Store Owner"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.requiresEmailVerification boolean 固定为 true,表示必须先完成邮箱验证码校验。
data.email string 标准化后的邮箱。
data.user User 新建用户。
data.sent boolean 是否已发送邮箱验证码。
data.expiresInSeconds integer 验证码剩余有效秒数。

错误:

响应 code data.apiCode 说明
400 invalid_payload 邮箱或密码格式错误。
400 duplicate_email 邮箱已注册。

备注:注册成功后会发送 6 位邮箱验证码,但不会签发登录 token只有 /api/auth/email/verify-code 校验成功后才会签发 Web Sanctum token。users.id 同时作为业务 account_id 使用。

POST /api/auth/login

鉴权:无。

入参:

位置 字段 类型 必填 说明
body email string 合法邮箱。
body password string 8-128 字符。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "owner@example.test",
    "password": "secure-password"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.user User 登录用户。邮箱未验证时也会返回,emailVerifiedAtnull
data.token string 邮箱已验证时返回的新签发 Sanctum Bearer token。
data.requiresEmailVerification boolean 邮箱未验证时返回 true
data.email string 邮箱未验证时返回的标准化邮箱。
data.sent boolean 邮箱未验证时表示是否已发送新的验证码。
data.expiresInSeconds integer 新验证码剩余有效秒数。
data.retryAfterSeconds integer 验证码重发限流时返回。

错误:

响应 code data.apiCode 说明
400 invalid_payload 入参格式错误。
401 invalid_credentials 邮箱或密码错误。

备注:邮箱已验证时,登录成功会先删除当前账号所有旧 Sanctum token包括旧 Web token 和扩展 token再签发新的 Web token扩展需要重新配对。邮箱未验证时不会返回 token会清理该账号旧 token 并返回 requiresEmailVerification=true ,前端应进入邮箱验证码流程。

GET /api/auth/me

鉴权Sanctum。仅 Web/Dashboard token 可调用,扩展 token 会被 dashboard.token 门禁拒绝。

入参:无。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/auth/me" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.user User|null 当前 token 对应用户。

POST /api/auth/logout

鉴权Sanctum。

入参:无。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/auth/logout" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok

备注:退出登录会删除当前账号所有 Sanctum token包括 Web token 和扩展 token让 Dashboard 和 Chrome 扩展同时下线。

POST /api/auth/rotate-session

鉴权Sanctum。

入参:无。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/auth/rotate-session" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.rotated boolean 固定为 false
data.reason string 固定为 sanctum_bearer_tokens_are_already_device_scoped

备注:兼容 Origin 的 Supabase session rotate 调用Sanctum 下是 no-op。

PATCH /api/auth/password

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
body password string 新密码8-128 字符。

请求示例:

curl -X PATCH "{BACKEND_API_BASE_URL}/api/auth/password" \
  -H "Authorization: Bearer <sanctum_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "password": "new-secure-password"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok

错误:

响应 code data.apiCode 说明
400 invalid_payload 密码长度不合法。

GET /api/auth/check-verified

鉴权:无。

入参:

位置 字段 类型 必填 说明
query email string 要查询验证状态的邮箱。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/auth/check-verified?email=owner%40example.test"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.verified boolean users.email_verified_at 是否非空。

错误:

响应 code data.apiCode 说明
400 invalid_email 邮箱格式错误。

备注:响应头包含 cache-control: no-store

POST /api/auth/email/verification-code

鉴权:无。

入参:

位置 字段 类型 必填 说明
body email string 要发送验证码的邮箱。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/auth/email/verification-code" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "owner@example.test"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.sent boolean 是否触发发送。未知邮箱也返回 true 以防账号枚举。
data.alreadyVerified boolean 已验证邮箱返回 true,此时 sent=false
data.expiresInSeconds integer 验证码剩余有效秒数,实际发送时返回。

错误:

响应 code data.apiCode 说明
400 invalid_email 邮箱格式错误。
429 too_many_requests 发送过于频繁,响应含 retryAfterSeconds

POST /api/auth/email/verify-code

鉴权:无。

入参:

位置 字段 类型 必填 说明
body email string 邮箱。
body code string 6 位数字验证码。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/auth/email/verify-code" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "owner@example.test",
    "code": "123456"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.verified boolean 成功为 true
data.user User 已验证用户。
data.token string 新签发 Sanctum Bearer token。

错误:

响应 code data.apiCode 说明
400 invalid_email 邮箱格式错误。
400 invalid_code 验证码格式错误、邮箱不存在、验证码不存在或验证码错误。
400 expired_code 验证码已过期。

备注:校验成功等价于一次 Web 登录,会删除当前账号所有旧 Web/扩展 token并签发新 Web token。

POST /api/auth/password/forgot

鉴权:无。

入参:

位置 字段 类型 必填 说明
body email string 要重置密码的邮箱。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/auth/password/forgot" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "owner@example.test"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.sent boolean 固定按成功处理;未知邮箱不发信但仍返回 true
data.expiresInSeconds integer 验证码有效秒数,实际发信时返回。

错误:

响应 code data.apiCode 说明
400 invalid_email 邮箱格式错误。
429 too_many_requests 发送过于频繁,响应含 retryAfterSeconds

POST /api/auth/password/reset

鉴权:无。

入参:

位置 字段 类型 必填 说明
body email string 邮箱。
body code string 6 位数字验证码。
body password string 新密码8-128 字符。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/auth/password/reset" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "owner@example.test",
    "code": "123456",
    "password": "new-secure-password"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.reset boolean 成功为 true
data.user User 重置密码后的用户。
data.token string 邮箱已验证时返回的新签发 Sanctum Bearer token。
data.requiresEmailVerification boolean 账号邮箱未验证时返回 true,此时不返回 token
data.email string 账号邮箱未验证时返回的标准化邮箱。
data.sent boolean 账号邮箱未验证时表示是否已发送邮箱验证验证码。
data.expiresInSeconds integer 邮箱验证验证码剩余有效秒数。
data.retryAfterSeconds integer 邮箱验证验证码重发限流时返回。

错误:

响应 code data.apiCode 说明
400 invalid_email 邮箱格式错误。
400 invalid_code 验证码格式错误、邮箱不存在、验证码不存在或验证码错误。
400 expired_code 验证码已过期。
400 invalid_payload 新密码不符合长度要求。

备注:成功后会消费该邮箱所有未使用重置码。若账号邮箱已验证,会删除当前账号所有旧 Web/扩展 token并签发新 Web token若账号邮箱未验证只更新密码、清理旧 token、发送邮箱验证验证码并返回 requiresEmailVerification=true

POST /api/auth/extension-pair

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
body label string 扩展设备标签,最多保留 120 字符。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/auth/extension-pair" \
  -H "Authorization: Bearer <sanctum_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "Chrome - Store Owner Laptop"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.token string 扩展专用 Sanctum plaintext token格式为 Sanctum 标准 <id>|<secret>
data.expiresAt string 扩展 token 过期时间。
data.userEmail string|null 当前用户邮箱。
data.apiBaseUrl string 后续扩展请求使用的 API base URL。
data.priorTokensRevoked integer 本次撤销的旧扩展 token 数量。
data.priorTokensFromOtherDevice boolean 是否踢掉了其他设备 token。

备注:扩展 token 明文只返回一次,数据库写入 personal_access_tokensnamestoreai_extensionstoreai_extension:<label>abilities 只包含扩展权限;新配对会删除该账号所有旧扩展 token保持扩展侧单设备策略但不会删除当前 Web token。普通 Dashboard Sanctum token 不能调用扩展接口。

POST /api/auth/refresh-extension-token

鉴权:扩展机器 token。 必需 headerAuthorization: Bearer <extension_sanctum_token>

入参:无。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/auth/refresh-extension-token" \
  -H "Authorization: Bearer <extension_sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.token string 新扩展专用 Sanctum plaintext token格式为 Sanctum 标准 <id>|<secret>
data.expiresAt string 新 token 过期时间。

错误:

响应 code data.apiCode 说明
401 unauthorized 缺少、格式错误、已删除、已过期或缺少对应扩展 ability 的 token。

备注:刷新成功会先创建新扩展 token再删除同账号其它扩展 token删除范围只限扩展 token不删除 Web token。

Dashboard 与 Onboarding

GET /api/dashboard/shell

鉴权Sanctum。

入参:无。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/dashboard/shell" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.user ApiUser 当前用户。
data.brand object|null 当前账号第一品牌的 shell 信息。
data.brand.id string 品牌 UUID。
data.brand.name string 品牌名。
data.brand.subscription_status string 订阅状态,枚举见 BrandCore.subscription_status
data.brand.trial_ends_at string|null 试用结束时间。
data.brand.subscription_cancel_at string|null 预约取消时间。
data.brand.is_comp boolean 是否内部赠送账号。

GET /api/dashboard

鉴权Sanctum。

入参:无。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/dashboard" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.user ApiUser 当前用户。
data.brand BrandCore|null 当前账号第一品牌。无品牌时只返回 userbrand:null
data.latestScan ScanRow|null 最近一次扫描。
data.previousScan ScanRow|null 最近一次可对比的历史成功扫描。
data.scanDots array 最近 12 次扫描轻量列表,元素 { id, scanned_at, status }
data.alerts AlertRow[] 最近扫描下的告警列表。
data.competitorMeta array 竞品元数据,元素 { product_url, label }
data.storeBrief StoreBrief|null 最近扫描 AI brief。
data.dismissedKeys string[] 当前品牌本地日已 Mark done 的 action key。
data.skuMatrixEntries array|null 当日 SKU Matrix 条目,来自 sku_matrix.matrix_json
data.skuMatrixConfidence string|null SKU Matrix 置信度。
data.gmvRows array 最近 40 条成功 scan 的 { scanned_at, payload }

GET /api/dashboard/settings

鉴权Sanctum。

入参:无。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/dashboard/settings" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.user ApiUser 当前用户。
data.brand BrandCore|null 设置页品牌信息;附加 thresholdsplatform_account_id_bound_atstores
data.manualUsedToday integer 品牌本地日内已使用手动扫描次数。
data.lastAutoScanAt string|null 最近一次 scheduled scan 时间。
data.competitors array 竞品列表,元素 { id, product_url, label, created_at }

GET /api/dashboard/billing

鉴权Sanctum。

入参:无。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/dashboard/billing" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.user ApiUser 当前用户。
data.brand BrandCore|null 账单页品牌信息,含 Stripe customer/subscription id 字段。
data.billing null 当前固定为 null保留给前端兼容。

GET /api/onboarding/status

鉴权Sanctum。

入参:无。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/onboarding/status" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.user ApiUser 当前用户。
data.brand BrandCore|null Onboarding 所需品牌信息。
data.lastScanAt string|null 当前店铺最近一次 okpartial scan 时间。

当前用户与扩展接口

POST /api/me/brand

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
body name string 品牌名称trim 后 1-80 字符。
body timezone string PHP 支持的时区名,最长 64 字符。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/me/brand" \
  -H "Authorization: Bearer <sanctum_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "StoreAI Demo Brand",
    "timezone": "Asia/Kuala_Lumpur"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.brand.id string 新品牌 UUID。
data.brand.name string 品牌名称。
data.brand.timezone string 品牌时区。
data.store.id string 同步创建的店铺 UUID。

错误:

响应 code data.apiCode 说明
400 invalid_payload 品牌名或时区格式错误。
400 brand_exists 当前账号已有品牌。

PATCH /api/me/brand

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
body name string 品牌名称trim 后 1-80 字符。
body timezone string PHP 支持的时区名,最长 64 字符。

请求示例:

curl -X PATCH "{BACKEND_API_BASE_URL}/api/me/brand" \
  -H "Authorization: Bearer <sanctum_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "StoreAI Demo Brand",
    "timezone": "Asia/Kuala_Lumpur"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.brand object 有变更时返回 { id, name, timezone }
data.noop boolean 没有可更新字段时返回 true

错误:

响应 code data.apiCode 说明
400 invalid_payload 品牌名或时区格式错误。
500 server_error 当前账号没有可更新品牌或更新后读取失败。

DELETE /api/me/account

鉴权Sanctum。

入参:无。

请求示例:

curl -X DELETE "{BACKEND_API_BASE_URL}/api/me/account" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok

错误:

响应 code data.apiCode 说明
409 subscription_active 品牌仍有 active 或 past_due 订阅,必须先取消或等待周期结束。

POST /api/me/action/dismiss

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
body action_key string Top Action key1-200 字符。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/me/action/dismiss" \
  -H "Authorization: Bearer <sanctum_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "action_key": "sku:SKU-001:fix_conversion"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok

错误:

响应 code data.apiCode 说明
400 no_brand 当前账号没有品牌。
400 invalid_payload action_key 缺失或长度非法。

备注:dismiss_date 使用品牌本地日开始时间转 UTC 后的日期键,与 Dashboard 和定时报表过滤逻辑一致。

GET /api/me/competitors

鉴权:扩展机器 token。 必需 headerAuthorization: Bearer <extension_sanctum_token>X-Ext-Version

入参:无。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/me/competitors" \
  -H "Authorization: Bearer <extension_sanctum_token>" \
  -H "X-Ext-Version: 0.1.0"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.competitors array 当前账号所有品牌已登记竞品。
data.competitors[].id string 竞品 UUID。
data.competitors[].brand_id string 品牌 UUID。
data.competitors[].product_url string Shopee 商品 URL。
data.competitors[].label string|null 竞品备注。

错误:

响应 code data.apiCode 说明
401 unauthorized 扩展 token 缺失或无效。
426 ext_outdated 扩展版本过旧。

GET /api/me/scan-quota

鉴权:扩展机器 token。 必需 headerAuthorization: Bearer <extension_sanctum_token>X-Ext-Version

入参:无。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/me/scan-quota" \
  -H "Authorization: Bearer <extension_sanctum_token>" \
  -H "X-Ext-Version: 0.1.0"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.allowed boolean 是否允许手动扫描。
data.reason string 不允许时返回,例如 subscription_requiredmanual_cap_reached
message string 不允许时的用户提示。
data.manualUsed integer 品牌本地日已使用手动扫描次数。
data.manualCap integer 每日手动扫描上限,默认 3。
data.remaining integer 剩余手动扫描次数。
data.boundPlatformAccountId string|null 品牌已绑定 Shopee seller id。
data.subState string 计算后的订阅状态。
data.trialEndsAt string|null 试用结束时间。
data.latestExtVersion string 后端配置的最新扩展版本。
data.extDownloadUrl string 扩展下载地址。

错误:

响应 code data.apiCode 说明
401 unauthorized 扩展 token 缺失或无效。
404 not_found 当前账号没有品牌或品牌没有店铺。
426 ext_outdated 扩展版本过旧。

POST /api/extension/heartbeat

鉴权:扩展机器 token。 必需 headerAuthorization: Bearer <extension_sanctum_token>

入参:

位置 字段 类型 必填 说明
body skip_reason string|null 扩展本轮跳过扫描原因,可为 null。
body ext_version string 扩展版本1-40 字符。
body tick_at string 客户端心跳时间,可被解析为 datetime。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/extension/heartbeat" \
  -H "Authorization: Bearer <extension_sanctum_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "skip_reason": "Shopee tab not open",
    "ext_version": "0.1.0",
    "tick_at": "2026-05-08T10:15:00Z"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
code string 更新失败时可能返回 update_failed

错误:

响应 code data.apiCode 说明
400 invalid_payload 请求体格式错误。
401 unauthorized 扩展 token 缺失或无效。
404 not_found 当前 token 所属账号没有品牌。

品牌设置接口

GET /api/brands/{id}/competitors

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
path id string 品牌 UUID。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/competitors" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.competitors array 竞品列表。无权访问品牌时返回空数组,不泄露品牌存在性。
data.competitors[].id string 竞品 UUID。
data.competitors[].product_url string Shopee 商品 URL。
data.competitors[].label string|null 竞品备注。
data.competitors[].created_at string|null 创建时间。

错误:

响应 code data.apiCode 说明
400 invalid_payload 品牌 id 不是 UUID。

POST /api/brands/{id}/competitors

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
path id string 品牌 UUID。
body productUrl string Shopee 商品页 URL必须匹配 -i.<shopId>.<itemId>
body label string|null 竞品备注,最多 80 字符。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/competitors" \
  -H "Authorization: Bearer <sanctum_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "productUrl": "https://shopee.com.my/example-product-i.123456.987654321",
    "label": "Main competitor"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.competitor.id string 新竞品 UUID。
data.competitor.product_url string Shopee 商品 URL。
data.competitor.label string|null 竞品备注。
data.competitor.created_at string|null 创建时间。

错误:

响应 code data.apiCode 说明
400 invalid_payload URL 或备注格式错误。
400 duplicate 已登记相同 URL。
400 over_limit 超过每品牌 10 个竞品上限。
404 not_found 品牌不存在或不属于当前用户。

DELETE /api/brands/{id}/competitors/{competitorId}

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
path id string 品牌 UUID。
path competitorId string 竞品 UUID。

请求示例:

curl -X DELETE "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/competitors/00000000-0000-4000-8000-000000000201" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok

错误:

响应 code data.apiCode 说明
400 invalid_payload path UUID 格式错误。

备注:删除是幂等的,行不存在或无权删除也返回成功。

PATCH /api/brands/{id}/notifications

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
path id string 品牌 UUID。
body morningBriefHour integer 早报本地小时0-23。
body eveningRecapHour integer 晚报本地小时0-23。
body pushMode string 兼容旧字段,只接受 daily_digestinstant_alert,不写库。
body dailyReportHour integer 兼容旧字段,仅校验,不写库。
body silentStartHour integer 兼容旧字段,仅校验,不写库。
body silentEndHour integer 兼容旧字段,仅校验,不写库。

请求示例:

curl -X PATCH "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/notifications" \
  -H "Authorization: Bearer <sanctum_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "morningBriefHour": 9,
    "eveningRecapHour": 18,
    "pushMode": "daily_digest",
    "dailyReportHour": 9,
    "silentStartHour": 22,
    "silentEndHour": 8
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.morning_brief_hour integer 更新后的早报小时,有实际更新时返回。
data.evening_recap_hour integer 更新后的晚报小时,有实际更新时返回。
data.noop boolean 没有可写字段时返回 true

错误:

响应 code data.apiCode 说明
400 invalid_payload 小时值不是整数、范围不在 0-23或同一请求内早晚报小时相同。
404 not_found 品牌不存在或不属于当前用户。

PATCH /api/brands/{id}/telegram

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
path id string 品牌 UUID。
body chatId string|null Telegram chat id必须匹配 ^-?\d{1,20}$;传 null 表示解绑。

请求示例:

curl -X PATCH "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/telegram" \
  -H "Authorization: Bearer <sanctum_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "chatId": "-1001234567890"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.telegramChatId string|null 更新后的 Telegram chat id。

错误:

响应 code data.apiCode 说明
400 invalid_payload chatId 缺失或格式错误。
404 not_found 品牌不存在或不属于当前用户。

POST /api/brands/{id}/telegram/pair

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
path id string 品牌 UUID。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/telegram/pair" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.token string 一次性配对 token格式 pair_<36hex>
data.expiresAt string 过期时间。
data.deeplink string Telegram https://t.me/<bot>?start=<token> 链接。
data.ttlSeconds integer 固定 900 秒。

错误:

响应 code data.apiCode 说明
404 not_found 品牌不存在或不属于当前用户。
500 misconfigured 缺少 TELEGRAM_BOT_HANDLE

PATCH /api/brands/{id}/thresholds

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
path id string 品牌 UUID。
body thresholds object 阈值覆盖对象;未知 key 会忽略;值为 null 表示移除覆盖。

请求示例:

curl -X PATCH "{BACKEND_API_BASE_URL}/api/brands/00000000-0000-4000-8000-000000000101/thresholds" \
  -H "Authorization: Bearer <sanctum_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "thresholds": {
      "SALES_01_GMV_DECLINE": 0.25,
      "SALES_01_MIN_ORDERS": 5,
      "SALES_02_ZERO_HOURS": 3,
      "SALES_04_DAYS_LEFT": 4,
      "SALES_05_RATING_MAX": 3,
      "AD_01_MIN_SPEND": 50,
      "AD_02_KEYWORD_SHARE": 0.5,
      "AD_03_ROAS_MIN": 2.5,
      "COMP_01_GAP": 0.12,
      "COMP_02_DROP": null
    }
  }'

允许的 thresholds key

key 类型 范围 说明
SALES_01_GMV_DECLINE number 0.1-0.5 GMV 下降阈值。
SALES_01_MIN_ORDERS integer 3-20 最小订单数。
SALES_02_ZERO_HOURS integer 1-6 连续零单小时数。
SALES_04_DAYS_LEFT integer 1-7 库存剩余天数。
SALES_05_RATING_MAX integer 1-4 低评分上限。
AD_01_MIN_SPEND integer 10-200 广告最低花费。
AD_02_KEYWORD_SHARE number 0.3-0.8 关键词花费占比。
AD_03_ROAS_MIN number 1.5-5.0 最低 ROAS。
COMP_01_GAP number 0.05-0.3 竞品价差阈值。
COMP_02_DROP number 0.05-0.3 竞品降价阈值。

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.thresholds object 合并保存后的品牌阈值覆盖对象。

错误:

响应 code data.apiCode 说明
400 invalid_payload thresholds 不是对象、值不是数字/null、整数 key 传小数或超出范围。
404 not_found 品牌不存在或不属于当前用户。

告警接口

POST /api/alerts/{id}/ack

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
path id string Alert UUID。
body ack boolean 是否确认;缺省为 true

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/alerts/00000000-0000-4000-8000-000000000301/ack" \
  -H "Authorization: Bearer <sanctum_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "ack": true
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.id string Alert UUID。
data.acknowledged boolean 更新后的确认状态。

错误:

响应 code data.apiCode 说明
400 invalid_payload Alert id 不是 UUID。
404 not_found Alert 不存在或不属于当前用户品牌。

扫描上传接口

POST /api/ingest/scan

鉴权:扩展机器 token。 必需 headerAuthorization: Bearer <extension_sanctum_token>X-Ext-Version

顶层入参:

位置 字段 类型 必填 说明
body brandId string 品牌 UUID。
body storeId string 店铺 UUID。
body scannedAt string 扫描时间,可解析为 datetime。
body payload object 扫描业务数据,见下方 schema。
body extractorStatus string okpartialfailed
body extractorErrors string[] 提取错误列表,最多 50 项,缺省 []
body trigger string manualscheduled,缺省 manual
body platformAccountId string|null Shopee seller id1-64 字符4 位以上纯数字才会参与绑定/比对。
body fieldMeta object 字段采集来源元数据,缺省 {}

payload.today

字段 类型 必填 说明
orders integer 今日订单数,>=0。
gmv_cents integer 今日 GMV 分,>=0。
cancel_rate number 取消率0-1。
return_rate number 退货率0-1。
gmv_delta_yesterday_pct number|null GMV 较昨日变化比例。
gmv_net_cents integer|null 净 GMV 分,>=0。
conversion_rate number|null 转化率。
visitors integer|null 访客数,>=0。
product_clicks integer|null 商品点击数,>=0。
aov_cents integer|null 客单价分,>=0。
orders_delta_yesterday_pct number|null 订单较昨日变化比例。
conversion_rate_delta_yesterday_pct number|null 转化率较昨日变化比例。
visitors_delta_yesterday_pct number|null 访客较昨日变化比例。
product_clicks_delta_yesterday_pct number|null 商品点击较昨日变化比例。
aov_delta_yesterday_pct number|null 客单价较昨日变化比例。

payload.skus[]

字段 类型 必填 说明
sku_id string SKU id。
name string SKU 名称。
price_cents integer|null 价格分,>=0。
l30d_sales integer|null 近 30 天销量,>=0。
l30d_impressions integer|null 近 30 天曝光,>=0。
impressions integer 曝光,>=0。
clicks integer 点击,>=0。
add_to_cart integer 加购,>=0。
orders integer 订单,>=0。
gmv_cents integer GMV 分,>=0。
stock integer 库存,>=0。
status string on_saleunlistedout_of_stock

payload.ads[]

字段 类型 必填 说明
campaign_id string 广告活动 id。
campaign_name string 广告活动名称。
type string 广告类型,缺省 unknown
state string ongoingpausedunknown,缺省 unknown
spend_cents integer 花费分,>=0。
clicks integer 点击,>=0。
orders integer 订单,>=0。
revenue_cents integer 收入分,>=0。
impressions integer 曝光,>=0缺省 0。
roas number ROAS>=0缺省 0。
target_roas number|null 目标 ROAS>=0。
daily_budget_cents integer|null 日预算分,>=0。
keywords array 关键词列表。
keywords[].keyword string 关键词。
keywords[].spend_cents integer 关键词花费分,>=0。
keywords[].clicks integer 关键词点击,>=0。
keywords[].orders integer 关键词订单,>=0。

其他 payload 字段:

字段 类型 必填 说明
store_id string 店铺 UUID。
scanned_at string 扫描时间。
recent_3h[] array 元素 { hour:string, orders:integer>=0, gmv_cents:integer>=0 }
reviews[] array 元素 { review_id, sku_id:null|string, rating:1-5, text, replied:boolean, created_at }
competitors[] array 元素 { product_url:url, price_cents:integer>=0, rating:null|number 0-5, sold_label:null|string }
review_summary object|null 包含 shop_ratingtotal_reviews_l30dunanswered_negativerating_histogram
product_aggregates object|null 数值字典,值可为 number 或 null。
traffic_sources array|null 流量来源数组,含 source、sales、clicks、conversion 等字段。
shop_health object|null 店铺健康对象;传 null 表示本次未采集,传 object 时内部字段见下表。

payload.shop_health

字段 类型 必填 说明
rating_overall string|null 店铺健康评级,允许 excellentgoodfairpoor 或 null。
penalty_points integer|null 当前罚分,>=0未知传 null。
penalty_threshold integer|null 触发风险提示的罚分阈值,>=1未知传 null。
metrics object 健康指标字典key 为指标名value 见下方 metrics.*
metrics.*.value number|null 指标当前值。
metrics.*.target number|null 指标目标值。
metrics.*.target_op string|null 达标比较符,允许 ltltegtgte 或 null。
metrics.*.counts_toward_score boolean 是否参与店铺健康评分。

fieldMeta 元素:

字段 类型 必填 说明
source string fetch_hookdomjson_ldog_metaaggregatemissing
selectorMatch string primaryfallbacktext_anchor
shopeeDataStamp string Shopee 数据戳。
ageMinutes number 数据年龄分钟数。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/ingest/scan" \
  -H "Authorization: Bearer <extension_sanctum_token>" \
  -H "X-Ext-Version: 0.1.0" \
  -H "Content-Type: application/json" \
  -d '{
    "brandId": "00000000-0000-4000-8000-000000000101",
    "storeId": "00000000-0000-4000-8000-000000000102",
    "scannedAt": "2026-05-08T10:15:00Z",
    "extractorStatus": "ok",
    "extractorErrors": [],
    "trigger": "manual",
    "platformAccountId": "1234567890",
    "fieldMeta": {
      "today.orders": {
        "source": "fetch_hook",
        "selectorMatch": "primary",
        "shopeeDataStamp": "seller-center-v1",
        "ageMinutes": 2
      },
      "ads[0].spend_cents": {
        "source": "dom",
        "selectorMatch": "fallback",
        "ageMinutes": 5
      }
    },
    "payload": {
      "store_id": "00000000-0000-4000-8000-000000000102",
      "scanned_at": "2026-05-08T10:15:00Z",
      "today": {
        "orders": 18,
        "gmv_cents": 258000,
        "cancel_rate": 0.02,
        "return_rate": 0.01,
        "gmv_delta_yesterday_pct": -0.18,
        "gmv_net_cents": 252000,
        "conversion_rate": 0.042,
        "visitors": 1200,
        "product_clicks": 260,
        "aov_cents": 14333,
        "orders_delta_yesterday_pct": -0.12,
        "conversion_rate_delta_yesterday_pct": -0.08,
        "visitors_delta_yesterday_pct": 0.06,
        "product_clicks_delta_yesterday_pct": -0.03,
        "aov_delta_yesterday_pct": -0.04
      },
      "recent_3h": [
        { "hour": "2026-05-08T08:00:00Z", "orders": 3, "gmv_cents": 42000 },
        { "hour": "2026-05-08T09:00:00Z", "orders": 0, "gmv_cents": 0 },
        { "hour": "2026-05-08T10:00:00Z", "orders": 1, "gmv_cents": 12000 }
      ],
      "skus": [
        {
          "sku_id": "SKU-001",
          "name": "Hydrating Serum 30ml",
          "price_cents": 6900,
          "l30d_sales": 180,
          "l30d_impressions": 12000,
          "impressions": 2800,
          "clicks": 210,
          "add_to_cart": 36,
          "orders": 12,
          "gmv_cents": 82800,
          "stock": 24,
          "status": "on_sale"
        }
      ],
      "ads": [
        {
          "campaign_id": "AD-001",
          "campaign_name": "Hero SKU Search Ads",
          "type": "search",
          "state": "ongoing",
          "spend_cents": 4200,
          "clicks": 96,
          "orders": 5,
          "revenue_cents": 34500,
          "impressions": 3600,
          "roas": 8.21,
          "target_roas": 5,
          "daily_budget_cents": 10000,
          "keywords": [
            { "keyword": "serum", "spend_cents": 1600, "clicks": 36, "orders": 1 },
            { "keyword": "hydrating serum", "spend_cents": 2600, "clicks": 60, "orders": 4 }
          ]
        }
      ],
      "reviews": [
        {
          "review_id": "REV-001",
          "sku_id": "SKU-001",
          "rating": 2,
          "text": "Bottle leaked during shipping",
          "replied": false,
          "created_at": "2026-05-08T04:00:00Z"
        }
      ],
      "competitors": [
        {
          "product_url": "https://shopee.com.my/example-product-i.123456.987654321",
          "price_cents": 6200,
          "rating": 4.8,
          "sold_label": "1.2k sold"
        }
      ],
      "review_summary": {
        "shop_rating": 4.7,
        "total_reviews_l30d": 42,
        "unanswered_negative": 3,
        "rating_histogram": { "1": 1, "2": 2, "3": 4, "4": 12, "5": 23 }
      },
      "product_aggregates": {
        "avg_price_cents": 7200,
        "active_sku_count": 18,
        "out_of_stock_count": 2
      },
      "traffic_sources": [
        {
          "source": "search",
          "sales": 12,
          "clicks": 180,
          "conversion_rate": 0.066,
          "gmv_cents": 168000
        }
      ],
      "shop_health": {
        "rating_overall": "good",
        "penalty_points": 0,
        "penalty_threshold": 3,
        "metrics": {
          "late_shipment_rate": {
            "value": 0.01,
            "target": 0.03,
            "target_op": "lte",
            "counts_toward_score": true
          },
          "chat_response_rate": {
            "value": 0.92,
            "target": 0.85,
            "target_op": "gte",
            "counts_toward_score": true
          }
        }
      }
    }
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.scanId string 新 scan UUID。
data.alertsEmitted integer 本次规则引擎产生的 alert 数量。

错误:

响应 code data.apiCode 说明
400 invalid_payload JSON 非法或 schema 校验失败。
401 unauthorized 扩展 token 缺失或无效。
402 subscription_required 当前订阅状态不允许扫描。
403 brand_mismatch 品牌不属于 token 账号,或店铺不属于品牌。
409 platform_account_mismatch 当前 Shopee seller id 与品牌已绑定 seller id 不一致。
426 ext_outdated 扩展版本过旧。
429 rate_limited 手动扫描达到每日上限。

备注:成功后会写入 scans、SKU/ad/competitor snapshots执行规则告警和 AI 派生数据生成;手动扫描会尝试发送 Telegram 回声scheduled 扫描会尝试补发 catch-up brief。

Cron 定时报表接口

GET|POST /api/cron/scheduled-report

鉴权Cron secret。生产环境要求 CRON_SECRET 已配置并通过 Authorization: Bearer <CRON_SECRET> 传入。非生产环境未配置 secret 时开放。

入参:无。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/cron/scheduled-report" \
  -H "Authorization: Bearer <CRON_SECRET>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.runAt string 本次 cron UTC 执行时间。
data.report.scannedBrands integer 扫描的品牌数量。
data.report.fired integer 实际发送报表数量。
data.report.skipped.wrong_hour integer 未到品牌本地发送窗口数量。
data.report.skipped.cooldown integer 仍在 22 小时冷却期或并发抢占失败数量。
data.report.skipped.no_channel integer 无 Telegram/WhatsApp 渠道数量。
data.report.skipped.no_store integer 无店铺数量。
data.report.skipped.invalid_config integer 报表小时配置非法数量。
data.report.errors integer 单品牌处理异常数量。
data.trialReport.scanned integer 扫描试用到期提醒的品牌数量。
data.trialReport.sent4h integer 发送 4 小时试用提醒数量。
data.trialReport.sent1h integer 发送 1 小时试用提醒数量。
data.trialReport.errors integer 试用提醒异常数量。

错误:

响应 code data.apiCode 说明
401 unauthorized Cron secret 不匹配。
503 misconfigured 生产环境未配置 CRON_SECRET

备注:服务端按品牌本地 morning_brief_hour / evening_recap_hour 判断发送窗口,并使用 last_morning_brief_at / last_evening_recap_at 做跨入口防重复。

GET /api/cron/daily-digest

鉴权:同 /api/cron/scheduled-report

入参:无。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/cron/daily-digest" \
  -H "Authorization: Bearer <CRON_SECRET>"

响应参数:完全复用 /api/cron/scheduled-report

备注:这是旧 daily digest URL 的兼容入口,当前直接复用 twice-daily scheduled-report 业务服务。

Stripe 接口

POST /api/stripe/checkout

鉴权Sanctum。

入参:

位置 字段 类型 必填 说明
body agreement_version string 条件必填 当前品牌首次订阅且尚未接受 SaaS 协议时必填,必须等于 SAAS_AGREEMENT_VERSION
header x-forwarded-for string 审计 IP优先取第一段。
header User-Agent string 协议接受审计 UA最多保存 500 字符。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/stripe/checkout" \
  -H "Authorization: Bearer <sanctum_token>" \
  -H "Content-Type: application/json" \
  -H "x-forwarded-for: 203.0.113.10, 10.0.0.1" \
  -H "User-Agent: StoreAI Web/1.0" \
  -d '{
    "agreement_version": "2"
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.url string Stripe Checkout URL。

错误:

响应 code data.apiCode 说明
400 no_brand 当前账号没有品牌。
400 agreement_required 首次订阅缺少协议版本。
409 agreement_stale 协议版本不是当前版本。
500 config_error 缺少 Stripe price id 配置。
500 stripe_error Stripe API 调用失败。
500 server_error Stripe 未返回 URL。

POST /api/stripe/portal

鉴权Sanctum。

入参:无。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/stripe/portal" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.url string Stripe Billing Portal URL。

错误:

响应 code data.apiCode 说明
400 no_customer 当前品牌没有 Stripe customer id。
500 stripe_error Stripe API 调用失败。
500 server_error Stripe 未返回 URL。

POST /api/stripe/webhook

鉴权Stripe 签名。请求必须保留 Stripe 原始 body。

入参:

位置 字段 类型 必填 说明
header Stripe-Signature string Stripe webhook 签名。
raw body event object Stripe event JSON。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/stripe/webhook" \
  -H "Stripe-Signature: t=1715150000,v1=<stripe_signature>" \
  -H "Content-Type: application/json" \
  -d '{
    "id": "evt_123",
    "type": "checkout.session.completed",
    "data": {
      "object": {
        "id": "cs_test_123",
        "customer": "cus_123",
        "metadata": {
          "brand_id": "00000000-0000-4000-8000-000000000101"
        },
        "subscription": "sub_123"
      }
    }
  }'

支持事件:

事件 处理
checkout.session.completed 写回品牌 stripe_customer_id
customer.subscription.created 更新订阅状态、subscription id、customer id、预约取消时间。
customer.subscription.updated 同上;首次进入 past_due 会尝试 Telegram 提醒。
customer.subscription.deleted 标记 canceled清空 subscription id 和预约取消时间。
charge.dispute.created 写入 dispute 审计,尝试通知管理员 Telegram。
charge.dispute.updated / charge.dispute.closed 更新 dispute 状态。

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.received string 已处理事件类型。
data.deduped boolean 重复已完成事件返回 true
data.eventId string 被去重的 Stripe event id。

错误:

响应 code data.apiCode 说明
400 missing_signature 缺少 Stripe-Signature
400 bad_signature 签名校验失败、webhook secret 缺失或 body 非法。
500 handler_error handler 异常HTTP 仍返回 200后端记录 failed 审计行,响应体表达失败。

Telegram 接口

POST /api/telegram/webhook

鉴权Telegram webhook secret。

入参:

位置 字段 类型 必填 说明
header X-Telegram-Bot-Api-Secret-Token string 必须等于后端 TELEGRAM_WEBHOOK_SECRET
body message.chat.id integer 条件必填 Telegram chat id无 chat id 会忽略。
body message.text string 条件必填 配对命令,格式 /start pair_<hex>
body message.from.first_name string 成功提示中使用。

请求示例:

curl -X POST "{BACKEND_API_BASE_URL}/api/telegram/webhook" \
  -H "X-Telegram-Bot-Api-Secret-Token: <TELEGRAM_WEBHOOK_SECRET>" \
  -H "Content-Type: application/json" \
  -d '{
    "update_id": 100001,
    "message": {
      "message_id": 10,
      "text": "/start pair_1234567890abcdef1234567890abcdef1234",
      "chat": {
        "id": -1001234567890,
        "type": "group",
        "title": "StoreAI Alerts"
      },
      "from": {
        "id": 123456,
        "first_name": "Owner"
      }
    }
  }'

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.brandId string 成功消费配对 token 后返回品牌 UUID。
data.ignored string 非配对或无法消费时返回原因:no_chat_idnot_pair_commandunknown_tokenalready_consumedexpiredrace_lost

错误:

响应 code data.apiCode 说明
400 invalid_payload 请求体不是合法 JSON。
401 unauthorized Secret header 不匹配。
503 misconfigured 后端未配置 Telegram webhook secret。

Debug 接口

Debug 路由默认关闭。必须配置 ENABLE_DEBUG_ROUTES=true 后才可访问;关闭时返回 404 { "code":0, "message":"Not found.", "data": { "error":"not_found" } }。开启后仍要求 Sanctum 鉴权。

GET /api/debug/last-scan

鉴权Debug 门禁 + Sanctum。

入参:无。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/debug/last-scan" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.diagnosis string 无品牌、无店铺、无扫描等状态下返回,例如 no_brandno_storeno_scans
data.message string 诊断说明。
data.brand object 品牌摘要含时区、报表小时、Telegram 是否存在。
data.latest_scan object 最新 scan 摘要 { id, scanned_at, status, error }
data.field_presence object|null payload 关键字段是否存在的统计。
data.audit_dump object|null 最近 scan 的 today、商品、广告、竞品等排障摘录。
data.latest_store_brief object|null 最近 scan 的 AI brief 原始摘要。
data.sku_matrix object|null 最近 SKU Matrixcomputed_forgenerated_atconfidencequadrant_countsentries
data.history_depth object 历史 scan 深度统计。
data.competitor_diag object 竞品配置和最近 10 次 scan 竞品字段统计。
data.rules_dry_run object|null 规则引擎只读 dry-run 结果。
data.ai object AI provider 与 key 存在性诊断,不返回真实 key。
data.live_verdict_reattempt object 只读 AI verdict 重试结果。
data.live_growth_reattempt object 只读 AI growth 重试结果。
data.diagnostics string[] 额外排障建议。

错误:

响应 code data.apiCode 说明
401 unauthorized 未登录。
404 not_found Debug 门禁未开启。

备注:该接口只读,不写 alertsstore_briefssku_matrix 或业务表。

GET /api/debug/test-verdict

鉴权Debug 门禁 + Sanctum。

入参:无。

请求示例:

curl -X GET "{BACKEND_API_BASE_URL}/api/debug/test-verdict" \
  -H "Authorization: Bearer <sanctum_token>"

响应参数:

字段 类型 说明
code integer 成功为 1
message string 成功为 ok
data.diagnosis string 诊断失败原因:no_storeno_scan_payloadno_api_keyunsupported_provider_for_debug
data.provider string 当前 DIAGNOSIS_PROVIDER
data.model string 实际请求 DeepSeek 的模型;deepseek-v4-flashdeepseek-reasoner 会改写为 deepseek-chat
data.api_key_length integer API key 长度,不返回真实 key。
data.request.endpoint string DeepSeek endpoint。
data.request.tool_name string 固定 emit_verdict
data.request.user_prompt_chars integer prompt 字符数。
data.response.http_status integer DeepSeek HTTP 状态码。
data.response.duration_ms integer 请求耗时毫秒。
data.response.network_error string|null 网络异常摘要。
data.response.body_preview string 响应前 2000 字符。
data.response.body_json_present boolean 响应是否可解析为 JSON。
data.tool_call_summary object|null tool call 摘要,含 finish reason、tool name、raw args、usage 等。

错误:

响应 code data.apiCode 说明
401 unauthorized 未登录。
404 not_found Debug 门禁未开启。

备注:该接口只支持 DeepSeek wire format 诊断Anthropic provider 请使用 /api/debug/last-scan 查看 provider 包装层诊断。