接口异常状态清单(App 端错误处理用)
这份文档给 App 端做"按不同异常显示不同提示"用。先看第 1 节的全局错误码速查——那是 App 应该据此分支的一组稳定 code;再看第 2 节的处理建议;第 3 节按页面列出每个接口具体会返回哪些异常。务必先读第 0 节的两个"坑",否则很容易漏判错误。
0. 怎么读这份文档(两个必须先知道的坑)
所有接口的返回都是统一信封:{ "success": bool, "code": "XXX", "message": "...", "data": ... }。但判错有两个反直觉的地方:
坑一:不能只看 HTTP 状态码。有一部分"业务失败"是 HTTP 200 但
success=false(后端用 ApiResponse.fail 直接返回)。比如创作者等级查用户不存在(USER_NOT_FOUND)就是 200。App 判错的第一依据必须是 success 字段,不是 HTTP 状态码。坑二:大部分业务错误的
code 都是 BAD_REQUEST,只有 message 不同。后端绝大多数业务校验是 IllegalArgumentException,统一映射成 400 / BAD_REQUEST,靠 message 文案区分(例如"invalid phone OTP""Coupon is expired""Withdraw amount is below minimum...")。所以:能靠稳定 code 分支的只有第 1 节那一小组;其余大量场景 App 只能直接把后端 message 展示给用户(这些 message 有的是中英双语、有的是纯英文,见第 4 节待改进点)。HTTP 状态码的大致含义:400 参数/业务校验失败 · 401 未登录/登录失效/被顶号 · 429 触发限流 · 404 资源不存在(少数接口) · 422 状态不允许(少数接口) · 502 三方平台报错 · 500 服务器内部错误或三方超时/配置问题(见坑三)。
坑三(给"网络/重试"提示用):转链接、查商品详情这类同步调三方(Shopee/TikTok/Lazada)的接口,三方超时(默认 10 秒)和 Shopee 报错会塌缩成 500
INTERNAL_SERVER_ERROR,跟真正的服务器 bug 长得一样。所以在这几个特定接口(见 3.4 标 调三方 的)上收到 500,更可能是"网络/三方抖动",适合显示"网络不稳定,请重试"而不是"系统错误"。1. 全局错误码速查(App 应该据此分支的稳定 code)
下面这些 code 是跨接口稳定、语义明确的,App 端应针对它们写专门的处理逻辑。其余 BAD_REQUEST 归为"直接展示 message"的兜底分支。
| code | HTTP | 含义 | 建议 App 处理 |
|---|---|---|---|
UNAUTHORIZED | 401 | 未登录 / token 无效 / 登录已过期(refresh 也失效) | 清本地登录态,跳登录页。文案"登录已过期,请重新登录" |
SESSION_REPLACED | 401 | 账号已在其它设备登录,当前设备被顶下线(单设备登录) | 清登录态跳登录页,文案专门写"你的账号已在其他设备登录",与普通过期区分 |
RATE_LIMITED | 429 | 请求过于频繁,触发网关限流 | 提示"操作太频繁,请稍后再试",做退避重试,不要立刻重试 |
TOO_MANY_REQUESTS | 429 | 发验证码等业务级频控(冷却/每小时上限) | 直接展示后端 message(已是中英双语),按钮进入倒计时冷却 |
PHONE_COUNTRY_NOT_SUPPORTED | 400 | 手机号国家不支持(印尼只支持 +62) | 直接展示 message(印尼语文案),提示换号 |
EXTERNAL_PLATFORM_ERROR | 502 | 三方平台(TikTok 等)明确报错 | "第三方暂时不可用,请稍后重试",可重试 |
ORDER_NOT_FOUND | 404 | 订单不存在(仅订单收益详情接口) | 展示"订单不存在"空态 |
NOT_FOUND | 404 | 路由/资源不存在 | 一般是接口路径问题,兜底错误提示 |
INTERNAL_SERVER_ERROR | 500 | 服务器内部错误或三方超时/后端配置缺失(见坑三) | 普通接口:"系统繁忙请稍后重试";标 调三方 接口:优先"网络不稳定,请重试" |
ADMIN_TOKEN_NOT_CONFIGURED | 503 | 内部/运营接口(App 用不到) | App 无需处理(仅运营端) |
HTTP 200 + success:false 类: USER_NOT_FOUND(创作者等级查用户不存在)—— 注意这类失败是 HTTP 200,App 必须先判 success 字段再判 HTTP 状态。 | |||
邀请绑定类(400,可据 code 分支给友好文案): INVITE_CODE_NOT_FOUND 邀请码无效 · SELF_INVITE_NOT_ALLOWED 不能邀请自己 · USER_ALREADY_BOUND_TO_ANOTHER_INVITER 已被他人邀请 · INVITER_ROLE_NOT_ALLOWED 邀请人不合格 · INVITE_RELATION_CYCLE_NOT_ALLOWED 会形成循环 · INVITE_RELATION_BIND_FAILED 绑定失败兜底。 | |||
密码重置类(400,均有独立 code): OTP_DISABLED · UNSUPPORTED_COUNTRY_CODE · ACCOUNT_NOT_FOUND · PASSWORD_TOO_SHORT · OTP_EXPIRED_OR_NOT_FOUND · OTP_ATTEMPTS_EXCEEDED · INVALID_OTP_CODE · OTP_ALREADY_CONSUMED · MISSING_OTP_INTENT_ID · OTP_INTENT_HAS_NO_BOUND_USER。 | |||
2. App 端处理建议(按异常类型归类做统一拦截)
建议在网络层做一个统一拦截器,先按下面顺序判定,再落到具体页面:
- 先判
success===false(不是先判 HTTP 状态),false 即失败,进入错误分支。 - 再看
code命中第 1 节稳定码 → 走对应专门逻辑(顶号跳登录、限流冷却、三方重试等)。 - 否则(多为
BAD_REQUEST)→ 直接 toast 后端message。这是覆盖面最大的兜底,所以后端 message 的可读性直接决定用户体验(见第 4 节,部分 message 目前是纯英文,已建议后端统一)。 - 401 系列一律清登录态跳登录,其中
SESSION_REPLACED用差异化文案。 - 标 调三方 的接口上的 500 → 归到"网络/重试"文案,而不是"系统错误",并提供重试按钮。
- 搜索/推荐类接口不会报三方错(后端已把三方失败降级成 200 +
warnings[])——App 拿到结果照常展示即可,可选择读warnings做灰度埋点,但不必给用户报错。
3.1 登录 / 注册页
| 接口 | 状态 | code | message / 触发 |
|---|---|---|---|
POST /api/v1/auth/phone/otp-intents发送手机验证码 | 400 | BAD_REQUEST | WhatsApp OTP is disabled / invalid Indonesian phone number / unsupported country code |
| 400 | PHONE_COUNTRY_NOT_SUPPORTED | Nomor ini tidak didukung. BeeX Indonesia hanya mendukung nomor Indonesia (+62). | |
| 429 | TOO_MANY_REQUESTS | 发送太频繁,请稍后再试(60s 冷却)/ 该号码验证码请求次数过多(5/时)/ 请求过于频繁(IP 100/时) | |
| 500 | INTERNAL_SERVER_ERROR | WA/短信发送通道异常(三方) | |
POST /api/v1/auth/phone/otp-intents/{id}/verify校验验证码登录 | 400 | BAD_REQUEST | phone OTP expired or not found / invalid phone OTP / phone OTP attempts exceeded / phone OTP already consumed |
| 401 | SESSION_REPLACED | (极少)登录成功后旧会话被顶 | |
POST /api/v1/auth/whatsapp/login-codes/consumeWhatsApp 登录码换 token | 400 | BAD_REQUEST | login code is required / invalid login code / login code not verified / login code conflict |
GET /api/v1/auth/whatsapp/login-intents/{id}轮询扫码登录状态 | 400 | BAD_REQUEST | login intent not found |
POST /api/v1/auth/password/login手机号+密码登录 | 400 | BAD_REQUEST | phone number not registered or account not found |
| 400 | BAD_REQUEST | login password not set, please use OTP login or set a password first | |
| 400 | BAD_REQUEST | incorrect password | |
POST /api/v1/auth/apple/native · /google/id-token · /tiktok/callback三方登录 | 400 | BAD_REQUEST | (Apple)invalid/expired Apple identity token 等十余种;(Google)Invalid Google identity token / audience mismatch / email not verified;(TikTok)TikTok login failed / invalid TikTok login state / expired TikTok login state |
| 502 | EXTERNAL_PLATFORM_ERROR | (Google/TikTok)三方 token 校验接口调用失败 | |
POST /api/v1/auth/refresh刷新 token | 401 | UNAUTHORIZED | 登录已过期(refresh token 失效/被吊销/过期,含被顶号后) |
密码重置(
/api/v1/auth/password-reset/*)有独立 code:OTP_DISABLED / UNSUPPORTED_COUNTRY_CODE / ACCOUNT_NOT_FOUND / PASSWORD_TOO_SHORT / OTP_EXPIRED_OR_NOT_FOUND / OTP_ATTEMPTS_EXCEEDED / INVALID_OTP_CODE / OTP_ALREADY_CONSUMED,均为 400,App 可据 code 给精准文案。3.2 安全设置 / 密码 / 注销
| 接口 | 状态 | code | message / 触发 |
|---|---|---|---|
GET /api/v1/user/security/status 等全部安全接口 | 400 | BAD_REQUEST | userId is required(未传 userId 且无 X-Beex-User-Id 头) |
POST /api/v1/user/security/pay-password/otp-intents发送设置支付密码验证码 | 400 | BAD_REQUEST | user not found: {userId} |
| 422 | UNPROCESSABLE_ENTITY | user has no phone or WhatsApp number: {userId} | |
POST /api/v1/user/security/pay-password/otp-intents/{id}/verify-and-set校验并设支付 PIN | 400 | BAD_REQUEST | pay password must be exactly 6 digits / OTP expired or not found / OTP attempts exceeded / invalid OTP code / OTP already consumed |
POST /api/v1/user/security/login-password/verify · /pay-password/verify校验密码是否正确 | 200 | OK | 密码错不是错误!返回 {valid:false},由 App 判断 valid 字段 |
POST /api/v1/account-deletion/intents发起注销 | 400 | BAD_REQUEST | userId is required / user not found |
| 422 | UNPROCESSABLE_ENTITY | account deletion is disabled / user is not active / official whatsapp number is not configured |
3.3 首页 / 商品浏览
好消息:商品搜索/推荐类接口对 App 几乎不报错。
/products/high-commission、/products/activity、/products/by-shop、/products/recommendations 内部调三方,但后端已把所有三方失败/超时降级成 HTTP 200 + warnings[],正常展示结果即可。唯一的错误是请求体里 countryCode/platform 枚举非法 → 400 BAD_REQUEST "Invalid request body"。| 接口 | 状态 | code | message / 触发 |
|---|---|---|---|
GET /api/v1/popular-brands · /campaigns/placements · /activity-campaigns/cards首页各配置/榜单(纯查库) | 400 | BAD_REQUEST | No enum constant ...CountryCode.XX(countryCode 非法,一般不会发生) |
GET /api/v1/new-user-activity/status新手任务 | 400 | BAD_REQUEST | userId 缺失 / countryCode 非法(用户不存在不报错,返回全 TODO) |
3.4 转链接 / 分享 / 邀请
本组是"网络/重试"文案的主战场。
/affiliate-links/generate 和 /products/resolve 会同步调三方:TikTok 明确报错 → 502(可直接提示"第三方暂不可用");而 Shopee 报错、以及任意三方 10 秒超时 → 500,跟真 bug 无法区分。这两个接口上收到 500,请优先按"网络不稳定,请重试"处理并给重试按钮。| 接口 | 状态 | code | message / 触发 |
|---|---|---|---|
POST /api/v1/affiliate-links/generate 调三方生成推广/分享链接 | 400 | BAD_REQUEST | Unsupported product platform: OTHER / Cannot extract TikTok product_id from productUrl... |
| 502 | EXTERNAL_PLATFORM_ERROR | TikTok Creator sharing link API error: code=... / TikTok Open API HTTP error: status=... | |
| 500 | INTERNAL_SERVER_ERROR | Shopee 报错、三方账号未授权/未配置、三方超时(message 被隐藏为 Internal server error)→ 按网络重试处理 | |
POST /api/v1/products/resolve 调三方解析链接查商品详情 | 400 | BAD_REQUEST | Unsupported product platform / Cannot extract Shopee itemId / Cannot extract TikTok product_id / TikTok product {id} has no inventory |
| 500 | INTERNAL_SERVER_ERROR | Shopee 未配置/未返回商品/报错/超时(TikTok、Lazada 会静默降级为兜底商品,不报错)→ 按网络重试处理 | |
| 200 | OK | TikTok/Lazada 失败时返回降级兜底商品,不报错 | |
POST /api/v1/shopee/product-offers 调三方Shopee 选品代理 | 400 | BAD_REQUEST | Shopee affiliate API is not configured |
| 500 | INTERNAL_SERVER_ERROR | Shopee 报错 / 超时 → 按网络重试处理 | |
POST /api/v1/links/parse解析链接(纯本地正则) | 400 | BAD_REQUEST | url 为空 / URL 非法(Illegal character in ...) |
GET /api/v1/affiliate-shares/{shareCode} · /api/v1/growth/invite-shares分享详情 / 生成邀请分享 | 400 | BAD_REQUEST | Share link not found. / user not found / countryCode does not match current user / BeeX currently only supports inviting USER |
POST /api/v1/rebate-fund/claim领返利金(绑邀请) | 400 | INVITE_CODE_NOT_FOUND 等 | 见第 1 节"邀请绑定类"专门 code:邀请码无效/不能邀请自己/已被他人邀请/循环等 |
3.5 钱包 / 提现 / 支付
提现是全 App 错误最丰富的接口,几乎全是
400 BAD_REQUEST + 明确 message,App 直接展示 message 即可(建议后端把这些改成带 code,见第 4 节)。注意:"提现已关闭""管理员/回调 token 未配置"等被 IllegalStateException 吞成 500,拿不到真实原因。| 接口 | 状态 | code | message / 触发 |
|---|---|---|---|
POST /api/v1/withdraw/apply发起提现 | 400 | BAD_REQUEST | Payment password is required before withdrawal / pay password must be exactly 6 digits / Invalid payment password |
| 400 | BAD_REQUEST | Withdraw amount is below minimum amount: N / exceeds per-transaction limit: N / exceeds daily amount limit: N / exceeds daily count limit: N | |
| 400 | BAD_REQUEST | Verified payout profile is required before withdrawal / Withdraw fee must be less than amount | |
| 500 | INTERNAL_SERVER_ERROR | 提现被后台关闭(真实原因被吞)/ Xendit 非余额不足类失败 → 提示稍后重试 | |
POST /api/v1/withdraw/payout-profile保存收款账户 | 400 | BAD_REQUEST | bankName/accountNumber 等必填校验 / countryCode 非法 |
GET /api/v1/withdraw/records/{id}提现记录详情 | 400 | BAD_REQUEST | Withdraw request not found(不存在或不属于本人,统一文案防枚举) |
POST /api/v1/payments/create创建支付/充值单 | 400 | BAD_REQUEST | Payment amount must be greater than zero / must be a whole currency amount / bizType 非法 |
| 500 | INTERNAL_SERVER_ERROR | 支付渠道未配置(真实原因被吞) | |
GET /api/v1/payments/{id}查支付单 | 400 | BAD_REQUEST | Payment order not found |
GET /api/v1/earnings/{userId}/summary · /commissions收益/佣金 | 400 | BAD_REQUEST | user not found: {userId} / unsupported commission category: {x} |
GET /api/v1/order-benefits/orders/{orderId}订单收益详情 | 404 | ORDER_NOT_FOUND | order not found(注意:这里是 404 + 专门 code,与提现的 400 not found 不同) |
GET /api/v1/wallets/{userId}钱包余额 | — | — | 无业务错误;钱包不存在返回 data:null + success:true |
3.6 我的 / 收益 / 优惠券
| 接口 | 状态 | code | message / 触发 |
|---|---|---|---|
GET /api/v1/me/summary我的页聚合 | 401 / 404 | UNAUTHORIZED / NOT_FOUND | authentication required(未登录)/ user not found(用户不存在) |
GET /api/v1/users/{userId}/profile · /me个人资料 | 404 / 400 | NOT_FOUND / BAD_REQUEST | user not found / userId is required |
PUT /api/v1/users/{userId}/profile改昵称/头像 | 400 | BAD_REQUEST | nickname must be 1-40 letters,numbers,spaces,or simple punctuation / avatarUrl is invalid |
| 400 | BAD_REQUEST | user not found | |
POST /api/v1/users/{userId}/avatar 传 OSS上传头像 | 400 | BAD_REQUEST | avatar file is required / avatar file must be <= 2MB / avatar must be jpeg,png,webp,or gif |
| 500 | INTERNAL_SERVER_ERROR | OSS 上传失败/未配置(真实原因被吞)→ 提示稍后重试 | |
POST /api/v1/coupons/preview优惠券试算 | 400 | BAD_REQUEST | Coupon not found / Coupon is expired / Coupon is not available / Coupon minimum order amount not reached / This is a discount coupon and cannot be used for cashback. 等(纯英文,建议直接展示) |
POST /api/v1/coupons/{id}/claim · /use · /cancel领/用/取消券 | 200 | OK | 失败不报错!返回 {claimed:false}/{used:false},由 App 判断布尔字段 |
GET /api/v1/growth/users/{userId}/creator-tier创作者等级 | 200 | USER_NOT_FOUND | (200 + success:false)user not found: {userId} |
POST /api/v1/growth/relations/bind绑定邀请关系 | 400 | SELF_INVITE_NOT_ALLOWED 等 | 见第 1 节"邀请绑定类"专门 code |
GET /api/v1/growth/users/{userId}/children · /children-earnings · /relation 等直邀用户 / 邀请关系 | — | — | 基本无业务错误 |
3.7 蜂蜜圈内容
| 接口 | 状态 | code | message / 触发 |
|---|---|---|---|
GET /api/v1/honey-feed/posts/{postId}帖子详情 | 400 | BAD_REQUEST | Honey feed post not found.(不存在或未发布) |
POST /api/v1/honey-feed/posts/{postId}/shares生成分享 | 400 | BAD_REQUEST | Honey feed post not found. / shareUserId is required. / shareUserId does not match current user. |
GET /api/v1/honey-feed/shares/{shareCode}解析分享 | 400 | BAD_REQUEST | Honey feed share not found. / Honey feed share is no longer available. |
GET /api/v1/honey-feed/posts · /posts/snapshot帖子列表(枚举参数) | 500 | INTERNAL_SERVER_ERROR | 注意:此接口 countryCode 是枚举参数,传非法值会变成 500(不是 400)——App 务必传合法 countryCode |
3.8 反馈 / 申诉 / 上传
| 接口 | 状态 | code | message / 触发 |
|---|---|---|---|
POST /api/v1/cashback-appeals返现申诉 | 400 | BAD_REQUEST | platformOrderId is required / whatsappNumber is required / cashback appeal attachment url is invalid |
| 注:本域没有"已提交过"去重,同一订单可重复提交 | |||
POST /api/v1/cashback-appeals/attachments · /api/v1/uploads/token · /api/v1/users/{id}/avatar 传 OSS各类文件上传 | 400 | BAD_REQUEST | file is required / file must be <= 10MB(申诉)/ <=2MB(头像)/ must be jpeg,png,webp,or gif / unsupported upload bizType |
| 500 | INTERNAL_SERVER_ERROR | OSS 未配置/签名失败/上传失败(真实原因被吞)→ 提示稍后重试 | |
POST /api/v1/app-logs/feedback · GET /app-logs/upload-token问题反馈/日志上传 | 400 | BAD_REQUEST | countryCode/description 必填 / urls 不能为空 / App log OSS upload is not configured |
3.9 推送 / App 配置 / 其它
| 接口 | 状态 | code | message / 触发 |
|---|---|---|---|
POST /api/v1/push/devices/register注册推送 token | 400 | BAD_REQUEST | must not be blank(countryCode/userId/deviceId/platform/pushToken)/ 枚举非法 |
GET /api/v1/app/runtime-config · /api/v1/countries启动配置/国家列表 | — | — | 无业务错误(参数容错,均有默认) |
GET /api/v1/app/h5-package/latestH5 包更新检查 | 400 | BAD_REQUEST | countryCode 非法(无可用包是正常 noUpdate 响应,不报错) |
GET/POST /api/v1/integrations/tiktok/* 调三方TikTok 授权/选品(运营向) | 502 | EXTERNAL_PLATFORM_ERROR | TikTok Open API HTTP error / TikTok API error(三方失败);参数缺失为 400 |
4. 后端已知待改进点(建议排期,不阻塞 App 接入)
下面几条是这次全量梳理时发现的、会直接影响 App 体验的一致性问题,建议后端后续统一,能让 App 端判错更简单:
- 大量业务错误共用
BAD_REQUEST,只能靠 message 区分。提现、优惠券、登录等关键路径建议逐步补上专门code(像密码重置那样),App 就能据 code 做多语言文案,而不是直接展示后端英文 message。 - 一批错误 message 是纯英文(优惠券、提现、个人资料校验等),App 直接展示会露出英文。建议后端要么补 code 让 App 自己多语言,要么把面向用户的 message 统一成印尼语/双语。
- 三方超时、Shopee 报错、OSS/配置失败被
IllegalStateException/超时异常吞成 500,真实原因只进日志不返回。建议后端把"三方不可用/超时"单独映射成一个可识别的 code(如UPSTREAM_TIMEOUT/UPSTREAM_UNAVAILABLE),App 就能精准显示"网络/重试"而不是猜。 - 两种"不存在"写法不一致:提现/支付用
400 "... not found",订单收益用404 ORDER_NOT_FOUND。建议统一。 - 枚举型
@RequestParam(如蜂蜜圈列表的 countryCode)传非法值会变 500 而非 400,与其它接口不一致。建议统一成 400。 - 部分"业务失败"用 HTTP 200 + success:false(如创作者等级 USER_NOT_FOUND),与其它 4xx 风格不一致——这条 App 侧只要坚持"先判 success 再判 HTTP"就不受影响,但值得后端知晓。