hyapp-server/docs/新版VIP权限策略_Flutter客户端对接文档.md

39 KiB
Raw Blame History

新版 VIP 权限策略 Flutter 客户端对接文档

1. VIP 权限策略

1.1 权限来源

  • VIP 等级只用于套餐名称、价格、有效期和等级展示。
  • Flutter 只使用 GET /api/v1/vip/me 返回的 data.state.effective_benefits 判断当前用户是否具有某项权益。
  • Flutter 不得使用 VIP等级 >= Nunlock_levelapp_code 或本地权益表推导权限。
  • /api/v1/vip/packages 中的 packages[].benefits 是该套餐当前配置的完整权益集合,只用于套餐页和升级引导,不能作为用户已拥有权益。
  • 写操作最终以对应业务接口的服务端鉴权结果为准。Flutter 的权益判断只用于入口显隐、锁态和交互预判。

1.2 Flutter 权益状态

class VipEntitlementState {
  final VipProgramConfig program;
  final VipIdentity effectiveVip;
  final String effectiveSource; // paid / trial / none
  final Map<String, VipBenefit> benefitsByCode;
  final VipUserSettings settings;
  final int evaluatedAtMs;

  bool has(String benefitCode) {
    return program.status == 'active' &&
        effectiveVip.active &&
        benefitsByCode[benefitCode]?.status == 'active';
  }

  bool allowsRoomEntryNotice() {
    return has('room_entry_notice') && settings.roomEntryNoticeEnabled;
  }

  bool allowsOnlineGlobalNotice() {
    return has('online_global_notice') &&
        settings.onlineGlobalNoticeEnabled;
  }

  bool allowsHideProfileData() {
    return has('hide_profile_data') && settings.hideProfileDataEnabled;
  }

  bool allowsAnonymousProfileVisit() {
    return has('anonymous_profile_visit') &&
        settings.anonymousProfileVisitEnabled;
  }

  bool allowsLeaderboardInvisible() {
    return has('leaderboard_invisible') &&
        settings.leaderboardInvisibleEnabled;
  }
}

benefitsByCode 必须由 state.effective_benefits 转换。未知 benefit_code 保留在状态中但不自动开放功能,不得导致解析失败或页面崩溃。

1.3 权益刷新

场景 Flutter 处理
登录成功、切换账号、切换 App 清空原 VIP 状态,调用 /vip/me
App 冷启动、回到前台 调用 /vip/me 校准过期、体验卡回落和后台配置变更
进入 VIP 页面 并发调用 /vip/packages/vip/me
购买、佩戴体验卡、卸下体验卡成功 使用响应中的 state 原子替换本地状态
收到 vip_notice event_id 去重,再调用 /vip/me
config_version 不一致 用户权限以 /vip/me.state 为准,重新请求 /vip/packages,不得合并不同版本的权益数组
VIP 到期倒计时结束 可更新页面显示,但执行功能前仍以服务端接口结果为准

购买、佩戴体验卡、卸下体验卡成功时,data.stateGET /api/v1/vip/medata.state 完全同构不是局部补丁。Flutter 必须整体替换 VIP store禁止按字段合并。若灰度期间收到缺少下文任一顶层字段的旧节点响应应放弃该响应中的 state 并立即重拉 /vip/me

1.4 Fami 初始等级权益

下表只用于识别初始配置。Flutter 实际必须渲染 /vip/packages 返回的数据。

等级 本级初始新增权益
VIP1 vip_badge_identityvip_medalavatar_framevip_title
VIP2 chat_bubblevip_giftentry_effectvisitor_history
VIP3 voice_waveprofile_cardcustom_room_backgroundroom_image_message
VIP4 animated_avataranimated_room_covermic_skinroom_bordervehicle
VIP5 colored_room_namecolored_nicknamecolored_idgift_tray_skin
VIP6 hide_profile_datacustom_avatar_frameleaderboard_invisibledaily_coin_rebate
VIP7 custom_profile_cardanonymous_profile_visitcustom_gift
VIP8 room_entry_noticecustom_pretty_idcustom_vehicle
VIP9 anti_kickanti_muteonline_global_notice

2. 通用协议

  • API 前缀:/api/v1
  • 请求头:Authorization: Bearer <access_token>
  • app_codeuser_id 从登录态解析Flutter 不传。
  • 时间字段统一使用 Unix epoch milliseconds。
  • 业务切日使用 UTCFlutter 仅在展示时转换本地时间。
  • 成功响应统一格式:
{
  "code": "OK",
  "message": "ok",
  "request_id": "req_xxx",
  "data": {}
}
  • request_id 只用于链路追踪。
  • command_id 的接口,网络重试必须复用原 command_id;新的业务动作必须生成新值。

2.1 VipState 唯一结构

GET /vip/meGET /vip/packages、购买、佩戴体验卡和卸下体验卡返回的 state 使用同一结构,固定包含以下 8 个顶层字段:

字段 含义
paid_vip 当前付费 VIP没有时为 null
equipped_trial_card 当前佩戴的体验卡;没有时为 null
effective_vip 付费 VIP 与体验卡合并后的当前展示身份
effective_source paidtrialnone
effective_benefits 服务端计算后的最终权益集合
evaluated_at_ms 本次状态计算时间
program_config 本次计算使用的完整 App VIP 配置
user_settings 当前 App 下完整的 5 项用户开关

paid_vipeffective_vip 非空时都返回完整 UserVip 字段:user_idlevelnameactivestarted_at_msexpires_at_msupdated_at_msprogram_typeconfig_versionprogram_config 返回完整字段,不允许写接口只返回 app_code/program_type 的裁剪对象。

2.2 特权展示协议

每项 VipBenefit 除执行字段外提供结构化 presentationFlutter 不解析 metadata_json,也不按 benefit_code 维护隐形展示配置:

{
  "description": "进入房间时展示动态背景和用户头像",
  "preview_items": [
    {
      "preview_id": "room_background_gold",
      "title": "鎏金房间背景",
      "media_type": "animation",
      "asset_url": "https://cdn.example/vip/room-bg.png",
      "preview_url": "https://cdn.example/vip/room-bg-preview.png",
      "animation_url": "https://cdn.example/vip/room-bg.svga",
      "composition_type": "user_avatar_center",
      "sort_order": 10
    }
  ],
  "numeric_reward": null
}
  • media_typeimageanimation;动态资源加载失败时依次使用 preview_urlasset_url
  • composition_typenoneuser_avatar_centeruser_avatar_waveform,分别表示无合成、头像居中、头像与声波纹合成。
  • preview_items 可返回多项,按 sort_order 升序、preview_id 去重。
  • numeric_reward 用于数值权益,字段为 label/value/unit/period,其中 periodonce/daily/monthly。该字段只用于展示,结算仍以后端接口为准。
  • 没有富预览时 presentationnullFlutter 回退展示权益名称和通用图标。

3. VIP 套餐

接口地址

GET /api/v1/vip/packages

参数

无。

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_packages",
  "data": {
    "current_vip": {
      "level": 3,
      "name": "VIP3",
      "active": true,
      "started_at_ms": 1780000000000,
      "expires_at_ms": 1782592000000,
      "program_type": "tiered_privilege_v1",
      "config_version": 7
    },
    "packages": [
      {
        "level": 4,
        "name": "VIP4",
        "status": "active",
        "price_coin": 4000,
        "duration_ms": 2592000000,
        "reward_resource_group_id": 0,
        "reward_items": [],
        "can_purchase": true,
        "sort_order": 40,
        "recharge_gate_required": false,
        "required_recharge_coin_amount": 0,
        "user_recharge_coin_amount": 0,
        "purchase_locked_reason": "",
        "benefits": [
          {
            "benefit_code": "custom_room_background",
            "name": "自定义房间背景",
            "benefit_type": "function",
            "unlock_level": 3,
            "status": "active",
            "trial_enabled": true,
            "resource_id": 0,
            "resource_type": "",
            "execution_scope": "room",
            "auto_equip": false,
            "sort_order": 110,
            "metadata_json": "",
            "presentation": {
              "description": "进入房间时展示动态背景和用户头像",
              "preview_items": [
                {
                  "preview_id": "room_background_gold",
                  "title": "鎏金房间背景",
                  "media_type": "animation",
                  "asset_url": "https://cdn.example/vip/room-bg.png",
                  "preview_url": "https://cdn.example/vip/room-bg-preview.png",
                  "animation_url": "https://cdn.example/vip/room-bg.svga",
                  "composition_type": "user_avatar_center",
                  "sort_order": 10
                }
              ],
              "numeric_reward": null
            }
          }
        ],
        "config_version": 7
      }
    ],
    "program_config": {
      "app_code": "fami",
      "program_type": "tiered_privilege_v1",
      "level_count": 9,
      "same_level_expiry_policy": "extend_remaining",
      "upgrade_expiry_policy": "replace_from_now",
      "downgrade_purchase_policy": "reject",
      "benefit_inheritance_policy": "target_only",
      "grant_mode": "trial_card",
      "trial_card_enabled": true,
      "status": "active",
      "config_version": 7
    },
    "state": {
      "paid_vip": {
        "user_id": 10001,
        "level": 3,
        "name": "VIP3",
        "active": true,
        "started_at_ms": 1780000000000,
        "expires_at_ms": 1782592000000,
        "updated_at_ms": 1780000000000,
        "program_type": "tiered_privilege_v1",
        "config_version": 7
      },
      "equipped_trial_card": null,
      "effective_vip": {
        "user_id": 10001,
        "level": 3,
        "name": "VIP3",
        "active": true,
        "started_at_ms": 1780000000000,
        "expires_at_ms": 1782592000000,
        "updated_at_ms": 1780000000000,
        "program_type": "tiered_privilege_v1",
        "config_version": 7
      },
      "effective_source": "paid",
      "effective_benefits": [
        {
          "benefit_code": "custom_room_background",
          "status": "active"
        }
      ],
      "evaluated_at_ms": 1780000000000,
      "program_config": {
        "app_code": "fami",
        "program_type": "tiered_privilege_v1",
        "level_count": 9,
        "same_level_expiry_policy": "extend_remaining",
        "upgrade_expiry_policy": "replace_from_now",
        "downgrade_purchase_policy": "reject",
        "benefit_inheritance_policy": "target_only",
        "grant_mode": "trial_card",
        "trial_card_enabled": true,
        "status": "active",
        "config_version": 7
      },
      "user_settings": {
        "app_code": "fami",
        "user_id": 10001,
        "room_entry_notice_enabled": true,
        "online_global_notice_enabled": true,
        "hide_profile_data_enabled": true,
        "anonymous_profile_visit_enabled": true,
        "leaderboard_invisible_enabled": true,
        "updated_at_ms": 0
      }
    }
  }
}

4. 当前 VIP 权限状态

接口地址

GET /api/v1/vip/me

参数

无。

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_vip_me",
  "data": {
    "level": 8,
    "name": "VIP8",
    "active": true,
    "started_at_ms": 1780100000000,
    "expires_at_ms": 1782692000000,
    "program_type": "tiered_privilege_v1",
    "config_version": 7,
    "program_config": {
      "app_code": "fami",
      "program_type": "tiered_privilege_v1",
      "level_count": 9,
      "same_level_expiry_policy": "extend_remaining",
      "upgrade_expiry_policy": "replace_from_now",
      "downgrade_purchase_policy": "reject",
      "benefit_inheritance_policy": "target_only",
      "grant_mode": "trial_card",
      "trial_card_enabled": true,
      "status": "active",
      "config_version": 7
    },
    "state": {
      "paid_vip": {
        "user_id": 10001,
        "level": 3,
        "name": "VIP3",
        "active": true,
        "started_at_ms": 1780000000000,
        "expires_at_ms": 1782592000000,
        "updated_at_ms": 1780000000000,
        "program_type": "tiered_privilege_v1",
        "config_version": 7
      },
      "equipped_trial_card": {
        "trial_card_id": "vip_trial_card_xxx",
        "entitlement_id": "ent_xxx",
        "resource_id": 108,
        "user_id": 10001,
        "level": 8,
        "name": "VIP8",
        "status": "active",
        "equipped": true,
        "duration_ms": 2592000000,
        "effective_at_ms": 1780100000000,
        "expires_at_ms": 1782692000000,
        "remaining_duration_ms": 2591000000,
        "grant_source": "admin",
        "source_grant_id": "grant_xxx",
        "created_at_ms": 1780100000000,
        "updated_at_ms": 1780100000000
      },
      "effective_vip": {
        "user_id": 10001,
        "level": 8,
        "name": "VIP8",
        "active": true,
        "started_at_ms": 1780100000000,
        "expires_at_ms": 1782692000000,
        "updated_at_ms": 1780100000000,
        "program_type": "tiered_privilege_v1",
        "config_version": 7
      },
      "effective_source": "trial",
      "effective_benefits": [
        {
          "benefit_code": "room_entry_notice",
          "name": "进房高亮通知",
          "benefit_type": "function",
          "status": "active",
          "trial_enabled": true,
          "resource_id": 0,
          "resource_type": "",
          "execution_scope": "room",
          "auto_equip": false,
          "sort_order": 290,
          "metadata_json": "",
          "presentation": null
        }
      ],
      "evaluated_at_ms": 1780101000000,
      "program_config": {
        "app_code": "fami",
        "program_type": "tiered_privilege_v1",
        "level_count": 9,
        "same_level_expiry_policy": "extend_remaining",
        "upgrade_expiry_policy": "replace_from_now",
        "downgrade_purchase_policy": "reject",
        "benefit_inheritance_policy": "target_only",
        "grant_mode": "trial_card",
        "trial_card_enabled": true,
        "status": "active",
        "config_version": 7
      },
      "user_settings": {
        "app_code": "fami",
        "user_id": 10001,
        "room_entry_notice_enabled": true,
        "online_global_notice_enabled": true,
        "hide_profile_data_enabled": true,
        "anonymous_profile_visit_enabled": true,
        "leaderboard_invisible_enabled": true,
        "updated_at_ms": 0
      }
    }
  }
}
  • effective_sourcepaidtrialnone
  • 当前展示身份只读 state.effective_vip
  • 当前权限只读 state.effective_benefits
  • daily_coin_rebate 不会因体验卡进入 effective_benefits

5. 购买、续期、升级

接口地址

POST /api/v1/vip/purchase

参数

{
  "command_id": "vip_purchase_10001_4_1780100000000",
  "level": 4
}
字段 必填 说明
command_id 最长 128 字节,同一次购买重试必须复用
level 必须来自 /vip/packages

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_purchase",
  "data": {
    "order_id": "vip_order_xxx",
    "transaction_id": "tx_xxx",
    "vip": {
      "level": 4,
      "name": "VIP4",
      "active": true,
      "started_at_ms": 1780100000000,
      "expires_at_ms": 1782692000000,
      "program_type": "tiered_privilege_v1",
      "config_version": 7
    },
    "coin_spent": 4000,
    "coin_balance_after": 6000,
    "coin_balance": {
      "asset_type": "COIN",
      "available_amount": 6000,
      "frozen_amount": 0,
      "version": 18
    },
    "reward_items": [],
    "program_config": {
      "app_code": "fami",
      "program_type": "tiered_privilege_v1",
      "level_count": 9,
      "same_level_expiry_policy": "extend_remaining",
      "upgrade_expiry_policy": "replace_from_now",
      "downgrade_purchase_policy": "reject",
      "benefit_inheritance_policy": "target_only",
      "grant_mode": "trial_card",
      "trial_card_enabled": true,
      "status": "active",
      "config_version": 7
    },
    "state": {
      "paid_vip": {
        "user_id": 10001,
        "level": 4,
        "name": "VIP4",
        "active": true,
        "started_at_ms": 1780100000000,
        "expires_at_ms": 1782692000000,
        "updated_at_ms": 1780100000000,
        "program_type": "tiered_privilege_v1",
        "config_version": 7
      },
      "equipped_trial_card": null,
      "effective_vip": {
        "user_id": 10001,
        "level": 4,
        "name": "VIP4",
        "active": true,
        "started_at_ms": 1780100000000,
        "expires_at_ms": 1782692000000,
        "updated_at_ms": 1780100000000,
        "program_type": "tiered_privilege_v1",
        "config_version": 7
      },
      "effective_source": "paid",
      "effective_benefits": [
        {
          "benefit_code": "custom_room_background",
          "status": "active"
        }
      ],
      "evaluated_at_ms": 1780100000000,
      "program_config": {
        "app_code": "fami",
        "program_type": "tiered_privilege_v1",
        "level_count": 9,
        "same_level_expiry_policy": "extend_remaining",
        "upgrade_expiry_policy": "replace_from_now",
        "downgrade_purchase_policy": "reject",
        "benefit_inheritance_policy": "target_only",
        "grant_mode": "trial_card",
        "trial_card_enabled": true,
        "status": "active",
        "config_version": 7
      },
      "user_settings": {
        "app_code": "fami",
        "user_id": 10001,
        "room_entry_notice_enabled": true,
        "online_global_notice_enabled": true,
        "hide_profile_data_enabled": true,
        "anonymous_profile_visit_enabled": true,
        "leaderboard_invisible_enabled": true,
        "updated_at_ms": 0
      }
    }
  }
}
  • 同级购买:从原截止时间继续累加。
  • Fami 高等级购买:立即替换低等级,从购买时间重新计时,低等级剩余时间丢弃。
  • 低等级购买:服务端拒绝。
  • 购买时佩戴体验卡:付费 VIP 会更新,但 effective_vip 仍可能来自体验卡,必须使用响应 state
  • 钱包缓存只使用 coin_balance。应用时比较 version,低版本响应不得覆盖本地高版本余额;coin_balance_after 仅保留旧客户端兼容。
  • 历史幂等订单在升级前没有记录版本时可能返回 coin_balance.version=0Flutter 必须调用现有钱包余额接口刷新,不得把版本 0 写入余额 store。

6. VIP 体验卡

6.1 体验卡列表

接口地址

GET /api/v1/users/me/resources?resource_type=vip_trial_card

参数

参数 位置 必填
resource_type query vip_trial_card

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_trial_cards",
  "data": {
    "items": [
      {
        "entitlement_id": "ent_card_a",
        "resource_id": 101,
        "resource": {
          "resource_id": 101,
          "resource_code": "vip_trial_card_3",
          "resource_type": "vip_trial_card",
          "name": "VIP3体验卡",
          "status": "active",
          "asset_url": "",
          "preview_url": "",
          "animation_url": "",
          "metadata_json": "{\"vip_level\":3}",
          "sort_order": 30
        },
        "status": "active",
        "quantity": 1,
        "remaining_quantity": 1,
        "effective_at_ms": 1780100000000,
        "expires_at_ms": 1781309600000,
        "source_grant_id": "grant_a",
        "created_at_ms": 1780100000000,
        "updated_at_ms": 1780100000000,
        "equipped": false
      }
    ],
    "total": 1
  }
}

6.2 佩戴体验卡

接口地址

POST /api/v1/vip/trial-cards/{entitlement_id}/equip

参数

参数 位置 必填 说明
entitlement_id path 使用体验卡列表中的实例 ID

无 body。

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_trial_equip",
  "data": {
    "trial_card": {
      "trial_card_id": "vip_trial_card_b",
      "entitlement_id": "ent_card_b",
      "resource_id": 102,
      "user_id": 10001,
      "level": 4,
      "name": "VIP4",
      "status": "active",
      "equipped": true,
      "duration_ms": 1728000000,
      "effective_at_ms": 1780200000000,
      "expires_at_ms": 1781928000000,
      "remaining_duration_ms": 1700000000,
      "grant_source": "admin",
      "source_grant_id": "grant_b",
      "created_at_ms": 1780200000000,
      "updated_at_ms": 1780200000000
    },
    "program_config": {
      "app_code": "fami",
      "program_type": "tiered_privilege_v1",
      "level_count": 9,
      "same_level_expiry_policy": "extend_remaining",
      "upgrade_expiry_policy": "replace_from_now",
      "downgrade_purchase_policy": "reject",
      "benefit_inheritance_policy": "target_only",
      "grant_mode": "trial_card",
      "trial_card_enabled": true,
      "status": "active",
      "config_version": 7
    },
    "state": {
      "paid_vip": {
        "user_id": 10001,
        "level": 3,
        "name": "VIP3",
        "active": true,
        "started_at_ms": 1780000000000,
        "expires_at_ms": 1782592000000,
        "updated_at_ms": 1780000000000,
        "program_type": "tiered_privilege_v1",
        "config_version": 7
      },
      "equipped_trial_card": {
        "trial_card_id": "vip_trial_card_b",
        "entitlement_id": "ent_card_b",
        "resource_id": 102,
        "user_id": 10001,
        "level": 4,
        "name": "VIP4",
        "status": "active",
        "equipped": true,
        "duration_ms": 1728000000,
        "effective_at_ms": 1780200000000,
        "expires_at_ms": 1781928000000,
        "remaining_duration_ms": 1700000000,
        "grant_source": "admin",
        "source_grant_id": "grant_b",
        "created_at_ms": 1780200000000,
        "updated_at_ms": 1780200000000
      },
      "effective_vip": {
        "user_id": 10001,
        "level": 4,
        "name": "VIP4",
        "active": true,
        "started_at_ms": 1780200000000,
        "expires_at_ms": 1781928000000,
        "updated_at_ms": 1780200000000,
        "program_type": "tiered_privilege_v1",
        "config_version": 7
      },
      "effective_source": "trial",
      "effective_benefits": [],
      "evaluated_at_ms": 1780228000000,
      "program_config": {
        "app_code": "fami",
        "program_type": "tiered_privilege_v1",
        "level_count": 9,
        "same_level_expiry_policy": "extend_remaining",
        "upgrade_expiry_policy": "replace_from_now",
        "downgrade_purchase_policy": "reject",
        "benefit_inheritance_policy": "target_only",
        "grant_mode": "trial_card",
        "trial_card_enabled": true,
        "status": "active",
        "config_version": 7
      },
      "user_settings": {
        "app_code": "fami",
        "user_id": 10001,
        "room_entry_notice_enabled": true,
        "online_global_notice_enabled": true,
        "hide_profile_data_enabled": true,
        "anonymous_profile_visit_enabled": true,
        "leaderboard_invisible_enabled": true,
        "updated_at_ms": 0
      }
    },
    "server_time_ms": 1780228000000
  }
}

6.3 卸下体验卡

接口地址

DELETE /api/v1/vip/trial-cards/equipped

参数

无。

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_trial_unequip",
  "data": {
    "unequipped": true,
    "program_config": {
      "app_code": "fami",
      "program_type": "tiered_privilege_v1",
      "level_count": 9,
      "same_level_expiry_policy": "extend_remaining",
      "upgrade_expiry_policy": "replace_from_now",
      "downgrade_purchase_policy": "reject",
      "benefit_inheritance_policy": "target_only",
      "grant_mode": "trial_card",
      "trial_card_enabled": true,
      "status": "active",
      "config_version": 7
    },
    "state": {
      "paid_vip": {
        "user_id": 10001,
        "level": 3,
        "name": "VIP3",
        "active": true,
        "started_at_ms": 1780000000000,
        "expires_at_ms": 1782592000000,
        "updated_at_ms": 1780000000000,
        "program_type": "tiered_privilege_v1",
        "config_version": 7
      },
      "equipped_trial_card": null,
      "effective_vip": {
        "user_id": 10001,
        "level": 3,
        "name": "VIP3",
        "active": true,
        "started_at_ms": 1780000000000,
        "expires_at_ms": 1782592000000,
        "updated_at_ms": 1780000000000,
        "program_type": "tiered_privilege_v1",
        "config_version": 7
      },
      "effective_source": "paid",
      "effective_benefits": [],
      "evaluated_at_ms": 1780229000000,
      "program_config": {
        "app_code": "fami",
        "program_type": "tiered_privilege_v1",
        "level_count": 9,
        "same_level_expiry_policy": "extend_remaining",
        "upgrade_expiry_policy": "replace_from_now",
        "downgrade_purchase_policy": "reject",
        "benefit_inheritance_policy": "target_only",
        "grant_mode": "trial_card",
        "trial_card_enabled": true,
        "status": "active",
        "config_version": 7
      },
      "user_settings": {
        "app_code": "fami",
        "user_id": 10001,
        "room_entry_notice_enabled": true,
        "online_global_notice_enabled": true,
        "hide_profile_data_enabled": true,
        "anonymous_profile_visit_enabled": true,
        "leaderboard_invisible_enabled": true,
        "updated_at_ms": 0
      }
    },
    "server_time_ms": 1780229000000
  }
}
  • 体验卡可自由切换,不比较等级高低。
  • 切换和卸下不会暂停、延长或删除体验卡。
  • 每张卡的剩余时间按绝对 expires_at_ms 计算。
  • 佩戴和卸下成功后立即使用响应 state 替换本地 VIP 状态。

7. VIP 功能开关

7.1 查询开关

接口地址

GET /api/v1/vip/settings

参数

无。

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_settings_get",
  "data": {
    "settings": {
      "app_code": "fami",
      "user_id": 10001,
      "room_entry_notice_enabled": true,
      "online_global_notice_enabled": false,
      "hide_profile_data_enabled": true,
      "anonymous_profile_visit_enabled": true,
      "leaderboard_invisible_enabled": true,
      "updated_at_ms": 1780300000000
    },
    "evaluated_at_ms": 1780300001000
  }
}

7.2 修改开关

接口地址

PATCH /api/v1/vip/settings

参数

局部更新,至少传一个字段:

{
  "room_entry_notice_enabled": false,
  "online_global_notice_enabled": true,
  "hide_profile_data_enabled": false,
  "anonymous_profile_visit_enabled": true,
  "leaderboard_invisible_enabled": true
}

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_settings_patch",
  "data": {
    "settings": {
      "app_code": "fami",
      "user_id": 10001,
      "room_entry_notice_enabled": false,
      "online_global_notice_enabled": true,
      "hide_profile_data_enabled": false,
      "anonymous_profile_visit_enabled": true,
      "leaderboard_invisible_enabled": true,
      "updated_at_ms": 1780300010000
    },
    "server_time_ms": 1780300010000
  }
}

5 个字段都使用 PATCH 语义:不传表示保持原值,显式 false 表示关闭。没有持久记录时均默认 true;设置只限制用户主动启用对应效果,不会单独授予 VIP 权益。服务端执行时使用“当前有效权益 AND 用户设置”判定Flutter 不能只看开关。

8. 每日 VIP 金币返现

8.1 查询当前返现

接口地址

GET /api/v1/vip/coin-rebates/current

参数

无。

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_rebate_current",
  "data": {
    "found": true,
    "rebate": {
      "rebate_id": "vip_rebate_xxx",
      "user_id": "10001",
      "task_day": "2026-07-15",
      "vip_level": 6,
      "vip_name": "VIP6",
      "coin_amount": 100,
      "status": "claimable",
      "available_at_ms": 1784073600000,
      "expires_at_ms": 1784160000000,
      "config_version": 8,
      "claimed_at_ms": 0,
      "wallet_transaction_id": "",
      "created_at_ms": 1784073605000,
      "updated_at_ms": 1784073605000
    },
    "server_time_ms": 1784073610000
  }
}

无资格时 found=falserebate=null

8.2 批量查询返现状态

接口地址

POST /api/v1/vip/coin-rebates/statuses

参数

{
  "rebate_ids": ["vip_rebate_xxx", "vip_rebate_yyy"],
  "page": 1,
  "page_size": 20
}
  • rebate_ids 最多 100 个。
  • 不传 rebate_ids 时按 page/page_size 查询历史。
  • page_size 最大 100。

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_rebate_statuses",
  "data": {
    "rebates": [
      {
        "rebate_id": "vip_rebate_xxx",
        "task_day": "2026-07-15",
        "vip_level": 6,
        "vip_name": "VIP6",
        "coin_amount": 100,
        "status": "claimable",
        "available_at_ms": 1784073600000,
        "expires_at_ms": 1784160000000,
        "claimed_at_ms": 0,
        "wallet_transaction_id": ""
      }
    ],
    "total": 1,
    "server_time_ms": 1784073610000
  }
}

8.3 领取返现

接口地址

POST /api/v1/vip/coin-rebates/{rebate_id}/claim

参数

参数 位置 必填 说明
rebate_id path 从 current、statuses 或系统消息取得
{
  "command_id": "vip_rebate_claim_10001_20260715"
}

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_rebate_claim",
  "data": {
    "rebate": {
      "rebate_id": "vip_rebate_xxx",
      "task_day": "2026-07-15",
      "vip_level": 6,
      "vip_name": "VIP6",
      "coin_amount": 100,
      "status": "claimed",
      "claimed_at_ms": 1784073700000,
      "wallet_transaction_id": "wallet_tx_xxx"
    },
    "transaction_id": "wallet_tx_xxx",
    "coin_balance": {
      "asset_type": "COIN",
      "available_amount": 20100,
      "frozen_amount": 0,
      "version": 18
    },
    "server_time_ms": 1784073700000
  }
}

9. 上线全服通知

接口地址

POST /api/v1/vip/online-notice

参数

{
  "command_id": "vip_online_<process_boot_id>_2026-07-15"
}
  • command_id 最长 128 字节。
  • 同一进程同一 UTC 日复用同一个 command_id
  • process_boot_id 只保存在内存中,不写磁盘。

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_online_notice",
  "data": {
    "event_id": "vip_online_notice:10001:sha256_digest",
    "group_id": "hy_fami_bc_g_v2",
    "status": "pending",
    "created": true,
    "evaluated_at_ms": 1784073610000
  }
}
  • status=pending 表示事件已进入异步投递链路。
  • 幂等命中时 created=false,不是失败。
  • 同一进程每天触发一次;杀掉进程后新进程当天可以再次触发。

10. 进房 VIP 通知

接口地址

POST /api/v1/rooms/join

参数

{
  "room_id": "room_1001",
  "command_id": "join_10001_room_1001_1784073600000",
  "role": "audience",
  "password": "",
  "response_mode": "full"
}

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_join",
  "data": {
    "result": {
      "applied": true,
      "room_version": 18,
      "server_time_ms": 1784073600000
    },
    "room": {},
    "viewer": {},
    "seats": [],
    "profiles": [],
    "im": {
      "group_id": "room_1001",
      "need_join_group": true
    },
    "effective_vip": {
      "program_type": "tiered_privilege_v1",
      "level": 8,
      "name": "VIP8",
      "room_entry_notice_enabled": true
    },
    "server_time_ms": 1784073600000
  }
}

本人进房首屏使用 HTTP 返回的 data.effective_vip。其他用户的进房效果使用当前房间 IM 消息。

11. VIP 装扮资源

11.1 我的资源列表

接口地址

GET /api/v1/users/me/resources?resource_type={resource_type}

参数

参数 位置 必填 说明
resource_type query avatar_frameprofile_cardvehiclechat_bubble 等资源类型

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_resources",
  "data": {
    "items": [
      {
        "entitlement_id": "ent_xxx",
        "resource_id": 108,
        "resource": {
          "resource_id": 108,
          "resource_code": "frame_vip9",
          "resource_type": "avatar_frame",
          "name": "VIP头像框",
          "status": "active",
          "asset_url": "https://cdn.example/frame.png",
          "preview_url": "https://cdn.example/frame-preview.png",
          "animation_url": "https://cdn.example/frame.svga",
          "metadata_json": "{}",
          "sort_order": 10
        },
        "status": "active",
        "expires_at_ms": 1786665600000,
        "equipped": true
      }
    ],
    "total": 1
  }
}

11.2 佩戴资源

接口地址

POST /api/v1/users/me/resources/{resource_id}/equip

参数

参数 位置 必填 说明
resource_id path 资源 ID

无 body。

11.3 卸下资源

接口地址

DELETE /api/v1/users/me/resources/{resource_type}/unequip

参数

参数 位置 必填 说明
resource_type path 资源类型

无 body。

11.4 当前装扮

接口地址

  • GET /api/v1/appearance
  • GET /api/v1/users/by-id/{user_id}/appearance

参数

第二个接口使用 path 参数 user_id

返回值

{
  "code": "OK",
  "message": "ok",
  "request_id": "req_appearance",
  "data": {
    "user_id": "10001",
    "avatar_frame": {},
    "profile_card": {},
    "vehicle": {},
    "mic_seat_animation": {},
    "equipped_badges": [],
    "badges": [],
    "resources": [],
    "server_time_ms": 1784073600000
  }
}

12. 相关 IM

12.1 VIP 激活通知

通道:腾讯云 IM C2C TIMCustomElem

{
  "Ext": "vip_notice",
  "Desc": "VipActivated",
  "Data": "{\"event_type\":\"VipActivated\",\"event_id\":\"evt_xxx\",\"app_code\":\"fami\",\"transaction_id\":\"tx_xxx\",\"command_id\":\"vip_purchase_10001_4_1780100000000\",\"user_id\":\"10001\",\"source\":\"vip_purchase\",\"action\":\"upgrade\",\"previous_level\":3,\"previous_expires_at_ms\":1781396000000,\"level\":4,\"vip_name\":\"VIP4\",\"started_at_ms\":1780100000000,\"expires_at_ms\":1782692000000,\"program_type\":\"tiered_privilege_v1\",\"config_version\":7,\"expiry_policy\":\"replace_from_now\",\"created_at_ms\":1780100000000}"
}

Flutter 按 event_id 去重,然后调用 GET /api/v1/vip/me,不能直接使用 IM 内容生成权限。

12.2 进房通知

通道:当前房间腾讯云 IM 群 TIMCustomElem,不发送到全服群。

{
  "Ext": "room_system_message",
  "Desc": "room_user_joined",
  "Data": "{\"event_id\":\"evt_join_xxx\",\"room_id\":\"room_1001\",\"event_type\":\"room_user_joined\",\"actor_user_id\":10001,\"target_user_id\":10001,\"room_version\":18,\"role\":\"audience\",\"nickname\":\"Nina\",\"avatar\":\"https://cdn.example/avatar.png\",\"display_user_id\":\"100042\",\"pretty_display_user_id\":\"888042\",\"vip_program_type\":\"tiered_privilege_v1\",\"effective_vip_level\":\"8\",\"effective_vip_name\":\"VIP8\",\"room_entry_notice_enabled\":\"true\"}"
}

仅当 event_type=room_user_joinedroom_entry_notice_enabled=true 时展示 VIP 进房效果。使用 event_id 去重。

12.3 上线全服通知

通道:全局播报群 TIMCustomElem

字段
Ext im_broadcast
Desc vip_online_notice
Data 下方 JSON 字符串
CloudCustomData Data 相同
{
  "event_id": "vip_online_notice:10001:sha256_digest",
  "broadcast_type": "vip_online_notice",
  "scope": "global",
  "app_code": "fami",
  "user_id": 10001,
  "sender_user_id": 10001,
  "nickname": "Nina",
  "avatar": "https://cdn.example/avatar.png",
  "avatar_frame_url": "https://cdn.example/frame.png",
  "avatar_frame_resource_id": 108,
  "avatar_frame_code": "frame_vip9",
  "avatar_frame_asset_url": "https://cdn.example/frame.png",
  "avatar_frame_preview_url": "https://cdn.example/frame-preview.png",
  "avatar_frame_animation_url": "https://cdn.example/frame-animation.svga",
  "display_user_id": "888042",
  "raw_display_user_id": "100042",
  "pretty_display_user_id": "888042",
  "vip_program_type": "tiered_privilege_v1",
  "vip_level": 9,
  "vip_name": "VIP9",
  "message": "欢迎进入Fami祝你有美好的一天",
  "action": {
    "type": "none"
  },
  "sent_at_ms": 1784073610500
}

Flutter 使用 event_id 去重直接使用事件内的用户、VIP 和头像框快照渲染。

12.4 金币返现系统消息

接口:GET /api/v1/messages?section=system&page_size=20&page_token=

{
  "message_id": "msg_vip_rebate_xxx",
  "title": "VIP金币返现",
  "summary": "尊敬的VIP用户平台今日返现100金币请及时领取。",
  "body": "尊敬的VIP用户平台今日返现100金币请及时领取。",
  "icon_url": "",
  "image_url": "",
  "action_type": "vip_coin_rebate_claim",
  "action_param": "{\"coin_amount\":100,\"expires_at_ms\":1784160000000,\"rebate_id\":\"vip_rebate_xxx\",\"task_day\":\"2026-07-15\"}",
  "read": false,
  "sent_at_ms": 1784073600000
}

Flutter 将 action_param 解析为 JSON打开页面后调用 /vip/coin-rebates/statuses 获取真实领取状态。

12.5 IM 登录与全局群

接口:GET /api/v1/im/usersig

Flutter 使用返回的 UserSig 登录腾讯云 IM并加入响应 join_groupstype=global_broadcast 的群组后,再消费 vip_online_notice

13. 稳定业务错误码

失败仍使用统一 envelopeFlutter 只判断 code,不得解析 message

{
  "code": "VIP_INSUFFICIENT_COIN",
  "message": "insufficient coin balance",
  "request_id": "req_purchase",
  "data": null
}
HTTP code 场景 Flutter 处理
409 VIP_PROGRAM_INACTIVE 当前 App 的 VIP 体系未启用 关闭购买/体验卡操作并刷新配置
409 VIP_PACKAGE_NOT_PURCHASABLE 套餐不存在、停用或不在当前体系范围 刷新 /vip/packages
409 VIP_DOWNGRADE_NOT_ALLOWED 购买等级低于当前付费等级 提示不可降级,刷新 /vip/me
409 VIP_RECHARGE_REQUIRED 未达到套餐充值门槛 展示充值引导
409 VIP_INSUFFICIENT_COIN 购买金币余额不足 展示金币充值入口
404 VIP_TRIAL_CARD_NOT_FOUND 卡不存在、不属于本人或背包权益已撤销 刷新体验卡列表和 /vip/me
409 VIP_TRIAL_CARD_EXPIRED 卡或背包权益已越过绝对截止时间 刷新体验卡列表和 /vip/me
403 VIP_BENEFIT_REQUIRED 操作需要的权益未生效或被用户关闭 刷新 /vip/me,保持入口锁态
403 VIP_ANTI_KICK 普通房主/管理员踢人被目标用户防踢拦截 提示目标不可被踢出,不改变房间本地状态
403 VIP_ANTI_MUTE 普通房主/管理员禁言被目标用户防禁言拦截 提示目标不可被禁言,不改变房间本地状态
404 VIP_COIN_REBATE_NOT_FOUND 返现不存在或不属于当前用户/App 刷新返现列表
409 VIP_COIN_REBATE_EXPIRED 已越过 UTC 次日 0 点的领取结束边界 标记过期并刷新返现列表
409 IDEMPOTENCY_CONFLICT 同一 command_id 被用于不同业务参数 停止重试,为新动作生成新 ID
409 LEDGER_CONFLICT 钱包并发版本冲突 刷新余额和 /vip/me 后由用户重新发起

返现重复领取采用幂等成功语义:相同 command_id 重试返回首次成功回执,不返回 VIP_REBATE_ALREADY_CLAIMED。因此客户端不应实现该错误分支。