syntax = "proto3";

package hyapp.wallet.v1;

option go_package = "hyapp.local/api/proto/wallet/v1;walletv1";

// DebitGiftRequest 是 room-service 送礼同步扣费的最小账务输入。
message DebitGiftRequest {
  string command_id = 1;
  string room_id = 2;
  int64 sender_user_id = 3;
  int64 target_user_id = 4;
  string gift_id = 5;
  int32 gift_count = 6;
  // price_version 为空时使用当前生效价格；非空时必须命中 active 价格版本。
  string price_version = 7;
  string app_code = 8;
  // region_id 来自 room-service 房间 visible_region_id，用于校验礼物区域可用性和匹配房间贡献比例。
  int64 region_id = 9;
  // target_is_host 由 gateway 根据 user-service active host profile 注入，客户端不能直接声明。
  bool target_is_host = 10;
  // target_host_region_id 是主播身份所属区域，用于后续按主播区域匹配工资政策。
  int64 target_host_region_id = 11;
  // target_agency_owner_user_id 是送礼时主播归属 Agency 的 owner，用于后续代理工资结算。
  int64 target_agency_owner_user_id = 12;
  // sender_region_id 是送礼用户所属区域快照；不能用房间 visible_region_id 替代。
  int64 sender_region_id = 13;
  // entitlement_id 非空表示本次送礼从用户背包 gift 权益扣库存，不扣 COIN。
  string entitlement_id = 14;
  // charge_source 区分 coin/bag；为空按 coin 兼容旧客户端。
  string charge_source = 15;
}

// DebitDirectGiftRequest 是私聊送礼的账务命令；gateway 负责用 user-service 补齐区域和主播身份。
message DebitDirectGiftRequest {
  string command_id = 1;
  int64 sender_user_id = 2;
  int64 target_user_id = 3;
  string gift_id = 4;
  int32 gift_count = 5;
  string price_version = 6;
  string app_code = 7;
  // region_id 使用 sender 当前 region_id；私聊没有房间可见区域，不能接收客户端自报区域。
  int64 region_id = 8;
  int64 sender_region_id = 9;
  bool target_is_host = 10;
  int64 target_host_region_id = 11;
  int64 target_agency_owner_user_id = 12;
  string entitlement_id = 13;
  string charge_source = 14;
}

// DebitGiftResponse 返回扣费流水、服务端结算值和 sender COIN 账后余额。
message DebitGiftResponse {
  string billing_receipt_id = 1;
  string transaction_id = 2;
  int64 coin_spent = 3;
  // gift_point_added 是历史回执字段；GIFT_POINT 已下线，新送礼固定返回 0。
  int64 gift_point_added = 4;
  int64 heat_value = 5;
  string price_version = 6;
  // balance_after 是 sender 被扣费资产 available_amount 账后余额。
  int64 balance_after = 7;
  string charge_asset_type = 8;
  int64 charge_amount = 9;
  string gift_type_code = 10;
  // host_period_diamond_added 是本次送礼给主播周期钻石账户增加的钻石；非主播为 0。
  int64 host_period_diamond_added = 11;
  // host_period_cycle_key 是 UTC 月周期，例如 2026-05；非主播为空。
  string host_period_cycle_key = 12;
  // cp_relation_type 只在 CP 礼物、兄弟礼物、姐妹礼物上有值，用于 room outbox 驱动关系申请。
  string cp_relation_type = 13;
  string gift_name = 14;
  string gift_icon_url = 15;
  string gift_animation_url = 16;
  repeated string gift_effect_types = 17;
  string entitlement_id = 18;
  string charge_source = 19;
  // gift_income_coin_amount 是本次给收礼人返还的 COIN；0 表示当前礼物类型/区域不返币。
  int64 gift_income_coin_amount = 20;
  // gift_income_balance_after 是收礼人 COIN 账后余额；未返币时为 0。
  int64 gift_income_balance_after = 21;
  // Deprecated: 仅用于旧 command_id 幂等重放返回首次 POINT 回执；新送礼不会再写入或消费该值。
  int64 host_point_added = 22 [deprecated = true];
  // Deprecated: 仅保留旧 POINT 回执的账后余额快照；新送礼不会再写入或消费该值。
  int64 host_point_balance_after = 23 [deprecated = true];
  // Deprecated: 仅保留旧 POINT 回执的资产类型快照；新送礼不会再写入或消费该值。
  string host_point_asset_type = 24 [deprecated = true];
  // Deprecated: 仅保留旧 POINT 回执的政策实例快照；新送礼不会再读取旧收益政策。
  string host_point_policy_instance_code = 25 [deprecated = true];
  // Deprecated: 仅保留旧 POINT 回执的模板编码快照。
  string host_point_template_code = 26 [deprecated = true];
  // Deprecated: 仅保留旧 POINT 回执的模板版本快照。
  string host_point_template_version = 27 [deprecated = true];
  // balance_version 是 sender 被扣费资产在本次账务完成后的版本；同 command_id 重放返回首次版本。
  int64 balance_version = 28;
  // recharge_seven_day_coins / recharge_thirty_day_coins 是扣费时由 wallet owner 固化的 UTC 自然日充值快照。
  // lucky-gift-service 只能消费这份结算事实做概率分层，不能为了开奖直连钱包库或临时扫描充值流水。
  int64 recharge_seven_day_coins = 29;
  int64 recharge_thirty_day_coins = 30;
  // last_recharged_at_ms 仅在真实 lucky/super_lucky 金币送礼时返回；0 表示近 30 个 UTC 自然日没有充值。
  int64 last_recharged_at_ms = 31;
  // paid_at_ms 是 wallet_transactions.created_at_ms 的稳定快照；首次扣费与同 command_id 重放必须完全一致。
  // room/lucky 只能用该 wallet owner 事实做 UTC 日、小时和充值后短窗归属，不能用 saga 或恢复 worker 时钟替代。
  int64 paid_at_ms = 32;
  // Host 政策无关收益快照：工资政策写 HOST_PERIOD_DIAMOND，永久积分类政策写 POINT_DIAMOND。
  string host_income_policy_type = 33;
  int64 host_income_balance_after = 36;
  string income_asset_type = 37;
  int64 host_base_amount = 38;
  int64 agency_amount = 39;
  int64 agency_owner_user_id = 40;
  int64 agency_income_balance_after = 41;
  uint64 host_income_policy_id = 42;
  uint64 host_income_policy_version = 43;
  string host_policy_coefficient = 44;
  int32 agency_point_share_bps = 45;
}

// DebitGiftTarget 是一笔批量送礼中的单个接收方账务快照。
message DebitGiftTarget {
  // command_id 是该目标的账务幂等键，由 room-service 从房间 command_id 派生。
  string command_id = 1;
  int64 target_user_id = 2;
  bool target_is_host = 3;
  int64 target_host_region_id = 4;
  int64 target_agency_owner_user_id = 5;
}

// BatchDebitGiftRequest 在一个钱包事务内完成同一 sender 对多个 target 的送礼扣费。
message BatchDebitGiftRequest {
  string command_id = 1;
  string room_id = 2;
  int64 sender_user_id = 3;
  string gift_id = 4;
  int32 gift_count = 5;
  string price_version = 6;
  string app_code = 7;
  // region_id 来自 room-service 房间 visible_region_id，用于校验礼物区域可用性和匹配房间贡献比例。
  int64 region_id = 8;
  int64 sender_region_id = 9;
  repeated DebitGiftTarget targets = 10;
  string entitlement_id = 11;
  string charge_source = 12;
}

// BatchDebitGiftReceipt 保留每个目标独立交易的回执，房间事件按它逐目标落事实。
message BatchDebitGiftReceipt {
  int64 target_user_id = 1;
  string command_id = 2;
  DebitGiftResponse billing = 3;
}

// BatchDebitGiftResponse 返回批量聚合值和逐目标账务回执。
message BatchDebitGiftResponse {
  DebitGiftResponse aggregate = 1;
  repeated BatchDebitGiftReceipt receipts = 2;
}

// DebitRobotGiftRequest 是机器人房间内部送礼扣费命令。
// 它只扣 ROBOT_COIN，不产生主播钻石、真实充值消费流水或普通礼物墙投影。
message DebitRobotGiftRequest {
  string command_id = 1;
  string room_id = 2;
  int64 sender_user_id = 3;
  int64 target_user_id = 4;
  string gift_id = 5;
  int32 gift_count = 6;
  string price_version = 7;
  string app_code = 8;
  int64 region_id = 9;
  int64 sender_region_id = 10;
}

// AssetBalance 是用户某类资产的余额投影。
message AssetBalance {
  string asset_type = 1;
  int64 available_amount = 2;
  int64 frozen_amount = 3;
  int64 version = 4;
  string app_code = 5;
}

// GetBalancesRequest 查询用户多资产余额；内部调用方负责鉴权和用户范围控制。
message GetBalancesRequest {
  string request_id = 1;
  int64 user_id = 2;
  repeated string asset_types = 3;
  string app_code = 4;
}

message GetBalancesResponse {
  repeated AssetBalance balances = 1;
}

message HostSalaryPolicyLevel {
  int32 level_no = 1;
  int64 required_diamonds = 2;
  int64 host_salary_usd_minor = 3;
  int64 host_coin_reward = 4;
  int64 agency_salary_usd_minor = 5;
  string status = 6;
  int32 sort_order = 7;
}

message HostSalaryPolicy {
  uint64 policy_id = 1;
  string name = 2;
  int64 region_id = 3;
  string status = 4;
  string settlement_mode = 5;
  string settlement_trigger_mode = 6;
  string gift_coin_to_diamond_ratio = 7;
  string residual_diamond_to_usd_rate = 8;
  int64 effective_from_ms = 9;
  int64 effective_to_ms = 10;
  repeated HostSalaryPolicyLevel levels = 11;
  string cycle_key = 12;
  uint64 policy_version = 13;
  string policy_type = 14;
  int64 point_diamonds_per_usd = 15;
  int64 coins_per_usd = 16;
  int64 minimum_withdraw_usd_minor = 17;
  int32 withdraw_fee_bps = 18;
  int32 agency_point_share_bps = 19;
  // 两个渠道的最低金额分别进入各自资金事务；旧 minimum_withdraw_usd_minor 仅保留为平台金额兼容别名。
  int64 coin_seller_minimum_withdraw_usd_minor = 20;
  int64 platform_minimum_withdraw_usd_minor = 21;
  // 只控制 POINT_DIAMOND -> COIN；关闭后永久积分余额和两种提现能力仍可独立使用。
  bool diamond_exchange_enabled = 22;
}

message GetActiveHostSalaryPolicyRequest {
  string request_id = 1;
  string app_code = 2;
  int64 region_id = 3;
  string settlement_mode = 4;
  string settlement_trigger_mode = 5;
  int64 now_ms = 6;
  // cycle_key 精确选择该工资周期已绑定的不可变政策版本；空值仅供旧客户端回退当前 UTC 月。
  string cycle_key = 7;
}

message GetActiveHostSalaryPolicyResponse {
  bool found = 1;
  HostSalaryPolicy policy = 2;
}

message HostSalaryProgress {
  int64 host_user_id = 1;
  string cycle_key = 2;
  int64 region_id = 3;
  int64 agency_owner_user_id = 4;
  int64 total_diamonds = 5;
  int64 gift_diamond_total = 6;
  int64 updated_at_ms = 7;
}

message GetHostSalaryProgressRequest {
  string request_id = 1;
  string app_code = 2;
  int64 host_user_id = 3;
  string cycle_key = 4;
  int64 now_ms = 5;
}

message GetHostSalaryProgressResponse {
  HostSalaryProgress progress = 1;
}

// TeamHostSalaryCycleStat 是一组 Agency 收款人名下主播在单个工资周期内的工资统计。
// estimated 口径是“预收入”：按当前累计钻石命中的政策档位工资 + 未达下一档剩余钻石按月底费率折美元，
// 与已结算累计取大，保证已关闭周期回读时等于真实发放值。
message TeamHostSalaryCycleStat {
  string cycle_key = 1;
  int64 estimated_host_salary_usd_minor = 2;
  int64 settled_host_salary_usd_minor = 3;
  // active_host_count 统计该周期内累计钻石大于 0 的主播数。
  int32 active_host_count = 4;
  int64 total_diamonds = 5;
}

message GetTeamHostSalaryStatsRequest {
  string request_id = 1;
  string app_code = 2;
  // agency_owner_user_ids 由调用方（gateway）从 user-service 组织树解析后传入；wallet 只按周期账户上的收款人快照聚合。
  repeated int64 agency_owner_user_ids = 3;
  repeated string cycle_keys = 4;
  // now_ms 用于解析各区域当前生效的工资政策做档位估算。
  int64 now_ms = 5;
}

message GetTeamHostSalaryStatsResponse {
  repeated TeamHostSalaryCycleStat stats = 1;
  int64 server_time_ms = 2;
}

// AdminCreditAssetRequest 是后台手动入账/调账的最小审计命令。
message AdminCreditAssetRequest {
  string command_id = 1;
  int64 target_user_id = 2;
  string asset_type = 3;
  int64 amount = 4;
  int64 operator_user_id = 5;
  string reason = 6;
  string evidence_ref = 7;
  string app_code = 8;
  string remark = 9;
}

message AdminCreditAssetResponse {
  string transaction_id = 1;
  AssetBalance balance = 2;
}

// AdminCreditCoinSellerStockRequest 是后台调整币商专用库存的账务命令。
message AdminCreditCoinSellerStockRequest {
  string command_id = 1;
  int64 seller_user_id = 2;
  string stock_type = 3;
  int64 coin_amount = 4;
  string paid_currency_code = 5;
  int64 paid_amount_micro = 6;
  string payment_ref = 7;
  string evidence_ref = 8;
  int64 operator_user_id = 9;
  string reason = 10;
  string app_code = 11;
  int64 seller_country_id = 12;
  int64 seller_region_id = 13;
}

message AdminCreditCoinSellerStockResponse {
  string transaction_id = 1;
  int64 seller_user_id = 2;
  string stock_type = 3;
  int64 coin_amount = 4;
  string paid_currency_code = 5;
  int64 paid_amount_micro = 6;
  bool counts_as_seller_recharge = 7;
  int64 balance_after = 8;
  int64 created_at_ms = 9;
}

// TransferCoinFromSellerRequest 是币商给玩家转普通金币的内部账务命令。
message TransferCoinFromSellerRequest {
  string command_id = 1;
  int64 seller_user_id = 2;
  int64 target_user_id = 3;
  int64 amount = 4;
  string reason = 5;
  string app_code = 6;
  // seller_region_id、target_region_id 和 target_country_id 由 gateway 从 user-service 查询，不接受客户端提交。
  int64 seller_region_id = 7;
  int64 target_region_id = 8;
  int64 target_country_id = 9;
}

// TransferCoinFromSellerResponse 同时返回金币到账结果和本次转账对应的充值金额口径。
message TransferCoinFromSellerResponse {
  string transaction_id = 1;
  int64 seller_balance_after = 2;
  int64 target_balance_after = 3;
  int64 amount = 4;
  int64 recharge_usd_minor = 5;
  string recharge_currency_code = 6;
  int64 recharge_policy_id = 7;
  string recharge_policy_version = 8;
  int64 recharge_policy_coin_amount = 9;
  int64 recharge_policy_usd_minor_amount = 10;
}

// TransferCoinSellerStockToChildRequest 是父币商向直属子币商转移专用金币库存的内部账务命令。
message TransferCoinSellerStockToChildRequest {
  string command_id = 1;
  int64 parent_user_id = 2;
  int64 child_user_id = 3;
  int64 amount = 4;
  string reason = 5;
  string app_code = 6;
  int64 region_id = 7;
}

message TransferCoinSellerStockToChildResponse {
  string transaction_id = 1;
  int64 parent_balance_after = 2;
  int64 child_balance_after = 3;
  int64 amount = 4;
  int64 created_at_ms = 5;
}

// CoinSellerSalaryExchangeRateTier 是用户工资转给币商时，按区域和美元金额命中的金币兑换区间。
message CoinSellerSalaryExchangeRateTier {
  int64 region_id = 1;
  int64 min_usd_minor = 2;
  int64 max_usd_minor = 3;
  int64 coin_per_usd = 4;
  string status = 5;
  int32 sort_order = 6;
  int64 updated_at_ms = 7;
}

message ListCoinSellerSalaryExchangeRateTiersRequest {
  string request_id = 1;
  string app_code = 2;
  int64 region_id = 3;
  bool include_disabled = 4;
}

message ListCoinSellerSalaryExchangeRateTiersResponse {
  repeated CoinSellerSalaryExchangeRateTier tiers = 1;
}

// ExchangeSalaryToCoinRequest 扣当前用户某个身份工资美元钱包，并按固定比例给同一用户加普通 COIN。
message ExchangeSalaryToCoinRequest {
  string command_id = 1;
  int64 user_id = 2;
  string salary_asset_type = 3;
  int64 salary_usd_minor = 4;
  string reason = 5;
  string app_code = 6;
}

message ExchangeSalaryToCoinResponse {
  string transaction_id = 1;
  int64 salary_balance_after = 2;
  int64 coin_balance_after = 3;
  int64 salary_usd_minor = 4;
  int64 coin_amount = 5;
  int64 coin_per_usd = 6;
}

// TransferSalaryToCoinSellerRequest 扣当前用户某个身份工资美元钱包，并按区域区间比例给同区域币商加专用金币库存。
message TransferSalaryToCoinSellerRequest {
  string command_id = 1;
  int64 source_user_id = 2;
  int64 seller_user_id = 3;
  string salary_asset_type = 4;
  int64 salary_usd_minor = 5;
  int64 region_id = 6;
  string reason = 7;
  string app_code = 8;
}

message TransferSalaryToCoinSellerResponse {
  string transaction_id = 1;
  int64 source_salary_balance_after = 2;
  int64 seller_balance_after = 3;
  int64 salary_usd_minor = 4;
  int64 coin_amount = 5;
  int64 coin_per_usd = 6;
  int64 rate_min_usd_minor = 7;
  int64 rate_max_usd_minor = 8;
}

// FreezeSalaryWithdrawalRequest 将用户工资美元从可用余额转入冻结余额，等待财务审核。
message FreezeSalaryWithdrawalRequest {
  string command_id = 1;
  int64 user_id = 2;
  string salary_asset_type = 3;
  int64 salary_usd_minor = 4;
  string reason = 5;
  string app_code = 6;
  string withdrawal_ref = 7;
  // region_id comes from the verified business identity and selects the cycle-bound Host policy.
  int64 region_id = 8;
}

message FreezeSalaryWithdrawalResponse {
  string transaction_id = 1;
  AssetBalance balance = 2;
  int64 salary_usd_minor = 3;
}

// SettleSalaryWithdrawalRequest 在审核通过时扣掉已冻结工资；不会再扣可用余额。
message SettleSalaryWithdrawalRequest {
  string command_id = 1;
  int64 user_id = 2;
  string salary_asset_type = 3;
  int64 salary_usd_minor = 4;
  int64 operator_user_id = 5;
  string reason = 6;
  string app_code = 7;
  string withdrawal_application_id = 8;
}

message SettleSalaryWithdrawalResponse {
  string transaction_id = 1;
  AssetBalance balance = 2;
  int64 salary_usd_minor = 3;
}

// ReleaseSalaryWithdrawalRequest 在审核拒绝或申请创建失败回滚时把冻结工资返还到可用余额。
message ReleaseSalaryWithdrawalRequest {
  string command_id = 1;
  int64 user_id = 2;
  string salary_asset_type = 3;
  int64 salary_usd_minor = 4;
  int64 operator_user_id = 5;
  string reason = 6;
  string app_code = 7;
  string withdrawal_application_id = 8;
}

message ReleaseSalaryWithdrawalResponse {
  string transaction_id = 1;
  AssetBalance balance = 2;
  int64 salary_usd_minor = 3;
}

// FreezePointWithdrawalRequest 将 POINT 从可用余额冻结到提现审核池。
message FreezePointWithdrawalRequest {
  string command_id = 1;
  int64 user_id = 2;
  // asset_type 只能是 POINT 或 COIN_SELLER_POINT；旧工资美元资产继续使用 SalaryWithdrawal 接口。
  string asset_type = 3;
  // gross_point_amount 是本次冻结的积分总额，最小值由 App + 区域 wallet 政策决定。
  int64 gross_point_amount = 4;
  // fee_point_amount 是平台手续费积分；为空时服务端按 fee_bps 计算。
  int64 fee_point_amount = 5;
  // net_point_amount 是预计到账积分，必须等于 gross - fee；为空时服务端计算。
  int64 net_point_amount = 6;
  // points_per_usd 是 Gateway 预览值；wallet 会在冻结事务内按已发布政策重新固化。
  int64 points_per_usd = 7;
  int32 fee_bps = 8;
  string reason = 9;
  string app_code = 10;
  string withdrawal_ref = 11;
  // region_id 只能由 gateway 从登录用户资料注入，客户端请求体不能决定 wallet 政策边界。
  int64 region_id = 12;
  // POINT_DIAMOND 由美元美分和 Host 政策换算冻结积分；历史 POINT 继续传 gross_point_amount。
  int64 gross_usd_minor = 13;
}

message FreezePointWithdrawalResponse {
  string transaction_id = 1;
  AssetBalance balance = 2;
  int64 gross_point_amount = 3;
  int64 fee_point_amount = 4;
  int64 net_point_amount = 5;
  int64 points_per_usd = 6;
  int32 fee_bps = 7;
  // policy_instance_code 是首次冻结事务实际使用的 wallet 政策实例，重放返回同一快照。
  string policy_instance_code = 8;
  int64 gross_usd_minor = 9;
  int64 fee_usd_minor = 10;
  int64 net_usd_minor = 11;
  uint64 policy_id = 12;
  uint64 policy_version = 13;
  int64 coins_per_usd = 14;
}

// SettlePointWithdrawalRequest 在审核通过时扣掉已冻结积分；不会再次扣 available。
message SettlePointWithdrawalRequest {
  string command_id = 1;
  int64 user_id = 2;
  string asset_type = 3;
  int64 gross_point_amount = 4;
  int64 fee_point_amount = 5;
  int64 net_point_amount = 6;
  int64 points_per_usd = 7;
  int32 fee_bps = 8;
  int64 operator_user_id = 9;
  string reason = 10;
  string app_code = 11;
  string withdrawal_application_id = 12;
  string freeze_transaction_id = 13;
}

message SettlePointWithdrawalResponse {
  string transaction_id = 1;
  AssetBalance balance = 2;
  int64 gross_point_amount = 3;
  int64 fee_point_amount = 4;
  int64 net_point_amount = 5;
  int64 points_per_usd = 6;
  int32 fee_bps = 7;
}

// ReleasePointWithdrawalRequest 在审核拒绝或申请创建失败时释放冻结积分。
message ReleasePointWithdrawalRequest {
  string command_id = 1;
  int64 user_id = 2;
  string asset_type = 3;
  int64 gross_point_amount = 4;
  int64 fee_point_amount = 5;
  int64 net_point_amount = 6;
  int64 points_per_usd = 7;
  int32 fee_bps = 8;
  int64 operator_user_id = 9;
  string reason = 10;
  string app_code = 11;
  string withdrawal_application_id = 12;
  // withdrawal_ref 只用于申请创建失败回滚对应的次数 reservation；人工驳回不传。
  string withdrawal_ref = 13;
  string freeze_transaction_id = 14;
}

message ReleasePointWithdrawalResponse {
  string transaction_id = 1;
  AssetBalance balance = 2;
  int64 gross_point_amount = 3;
  int64 fee_point_amount = 4;
  int64 net_point_amount = 5;
  int64 points_per_usd = 6;
  int32 fee_bps = 7;
}

// Resource 是资源库里可配置、可赠送、可被礼物引用的最小资源。
message Resource {
  string app_code = 1;
  int64 resource_id = 2;
  string resource_code = 3;
  string resource_type = 4;
  string name = 5;
  string status = 6;
  bool grantable = 7;
  string grant_strategy = 8;
  string wallet_asset_type = 9;
  int64 wallet_asset_amount = 10;
  repeated string usage_scopes = 11;
  string asset_url = 12;
  string preview_url = 13;
  string animation_url = 14;
  string metadata_json = 15;
  int32 sort_order = 16;
  int64 created_by_user_id = 17;
  int64 updated_by_user_id = 18;
  int64 created_at_ms = 19;
  int64 updated_at_ms = 20;
  bool manager_grant_enabled = 21;
  string price_type = 22;
  int64 coin_price = 23;
  // gift_point_amount 是历史字段；资源和礼物价格现在只维护真实 COIN 价格，新写入固定为 0。
  int64 gift_point_amount = 24;
}

message ResourceGroupItem {
  int64 group_item_id = 1;
  int64 group_id = 2;
  int64 resource_id = 3;
  Resource resource = 4;
  int64 quantity = 5;
  int64 duration_ms = 6;
  int32 sort_order = 7;
  int64 created_at_ms = 8;
  int64 updated_at_ms = 9;
  string item_type = 10;
  string wallet_asset_type = 11;
  int64 wallet_asset_amount = 12;
}

message ResourceGroup {
  string app_code = 1;
  int64 group_id = 2;
  string group_code = 3;
  string name = 4;
  string status = 5;
  string description = 6;
  int32 sort_order = 7;
  repeated ResourceGroupItem items = 8;
  int64 created_by_user_id = 9;
  int64 updated_by_user_id = 10;
  int64 created_at_ms = 11;
  int64 updated_at_ms = 12;
}

// ResourceGroupSnapshot 是 owner 在业务发布时钉住的不可变奖励承诺。后续资源组编辑、停用
// 或资源展示信息变化都不会改变 snapshot_id 对应的实际发放内容和 App 展示内容。
message ResourceGroupSnapshot {
  string app_code = 1;
  string snapshot_id = 2;
  string pin_key = 3;
  int64 source_group_id = 4;
  int64 version_no = 5;
  string snapshot_hash = 6;
  ResourceGroup group = 7;
  int64 created_by_user_id = 8;
  int64 created_at_ms = 9;
  int64 source_group_updated_at_ms = 10;
  // source_content_hash 覆盖资源组、组项和嵌套资源的完整规范化内容；成员资源独立编辑也会改变该值。
  string source_content_hash = 11;
}

message PinResourceGroupSnapshotRequest {
  string request_id = 1;
  string app_code = 2;
  int64 group_id = 3;
  // pin_key 由发布方按业务版本生成；重试同一 key 必须返回同一快照。
  string pin_key = 4;
  int64 operator_user_id = 5;
  // wallet 锁住来源组后必须再次匹配，避免 Get->Pin 间编辑竞态固化错误版本。
  int64 expected_group_updated_at_ms = 6;
  // GetResourceGroup 返回的 owner 内容版本。wallet 在事务内锁住组、组项和资源后重新计算并匹配。
  string expected_source_content_hash = 7;
  // required_region_ids 是发布方要求奖励可发送的可信 active region 集合；wallet 对 gift resource
  // 按 pin-time gift config 区域并集逐区 fail-close。空集合仅供不带区域语义的旧调用。
  repeated int64 required_region_ids = 8;
  // required_all_regions=true 时必须存在 region_id=0 的 pin-time gift config，确保发布后新增区域也可用。
  bool required_all_regions = 9;
}

message PinResourceGroupSnapshotResponse {
  ResourceGroupSnapshot snapshot = 1;
}

message GiftConfig {
  string app_code = 1;
  string gift_id = 2;
  int64 resource_id = 3;
  Resource resource = 4;
  string status = 5;
  string name = 6;
  int32 sort_order = 7;
  string presentation_json = 8;
  string price_version = 9;
  int64 coin_price = 10;
  // gift_point_amount 是历史字段；送礼结算不再读取该值，新写入固定为 0。
  int64 gift_point_amount = 11;
  int64 heat_value = 12;
  int64 created_by_user_id = 13;
  int64 updated_by_user_id = 14;
  int64 created_at_ms = 15;
  int64 updated_at_ms = 16;
  repeated int64 region_ids = 17;
  string gift_type_code = 18;
  string charge_asset_type = 19;
  int64 effective_from_ms = 20;
  int64 effective_to_ms = 21;
  repeated string effect_types = 22;
  string cp_relation_type = 23;
  // vip_level 是 VIP/贵族礼物对应的 VIP 等级；非 VIP 礼物固定为 0。
  int32 vip_level = 24;
}

message GiftTypeConfig {
  string app_code = 1;
  string type_code = 2;
  string name = 3;
  string tab_key = 4;
  string status = 5;
  int32 sort_order = 6;
  int64 created_by_user_id = 7;
  int64 updated_by_user_id = 8;
  int64 created_at_ms = 9;
  int64 updated_at_ms = 10;
}

message ResourceShopItem {
  string app_code = 1;
  int64 shop_item_id = 2;
  int64 resource_id = 3;
  Resource resource = 4;
  string status = 5;
  int32 duration_days = 6;
  string price_type = 7;
  int64 coin_price = 8;
  int64 effective_from_ms = 9;
  int64 effective_to_ms = 10;
  int32 sort_order = 11;
  int64 created_by_user_id = 12;
  int64 updated_by_user_id = 13;
  int64 created_at_ms = 14;
  int64 updated_at_ms = 15;
}

message UserResourceEntitlement {
  string app_code = 1;
  string entitlement_id = 2;
  int64 user_id = 3;
  int64 resource_id = 4;
  Resource resource = 5;
  string status = 6;
  int64 quantity = 7;
  int64 remaining_quantity = 8;
  int64 effective_at_ms = 9;
  int64 expires_at_ms = 10;
  string source_grant_id = 11;
  int64 created_at_ms = 12;
  int64 updated_at_ms = 13;
  bool equipped = 14;
  string source_snapshot_id = 15;
  string resource_snapshot_hash = 16;
  // pinned_gift_configs 仅在 source_snapshot_id 非空且资源类型为 gift 时返回。内容已由 wallet
  // 校验 hash/resource/app 绑定，App 背包必须优先使用它，legacy entitlement 才回退当前目录。
  repeated GiftConfig pinned_gift_configs = 17;
  // vip_benefit_codes 是该资源在当前用户有效 VIP 等级下命中的真实权益编码。
  // 展示层用它区分同为 badge 的 VIP 身份标识、勋章和普通徽章，禁止按 URL 或名称猜测。
  repeated string vip_benefit_codes = 18;
}

message ResourceGrantItem {
  int64 grant_item_id = 1;
  string grant_id = 2;
  int64 resource_id = 3;
  string resource_snapshot_json = 4;
  int64 quantity = 5;
  int64 duration_ms = 6;
  string result_type = 7;
  string wallet_transaction_id = 8;
  string entitlement_id = 9;
  int64 created_at_ms = 10;
}

message ResourceGrant {
  string app_code = 1;
  string grant_id = 2;
  string command_id = 3;
  int64 target_user_id = 4;
  string grant_source = 5;
  string grant_subject_type = 6;
  string grant_subject_id = 7;
  string status = 8;
  string reason = 9;
  int64 operator_user_id = 10;
  repeated ResourceGrantItem items = 11;
  int64 created_at_ms = 12;
  int64 updated_at_ms = 13;
  int64 revoked_at_ms = 14;
  int64 revoked_by_user_id = 15;
  string revoke_reason = 16;
}

message ResourceGroupItemInput {
  int64 resource_id = 1;
  int64 quantity = 2;
  int64 duration_ms = 3;
  int32 sort_order = 4;
  string item_type = 5;
  string wallet_asset_type = 6;
  int64 wallet_asset_amount = 7;
}

message ListResourcesRequest {
  string request_id = 1;
  string app_code = 2;
  string resource_type = 3;
  string status = 4;
  string keyword = 5;
  int32 page = 6;
  int32 page_size = 7;
  bool active_only = 8;
  bool manager_grant_only = 9;
}

message ListResourcesResponse {
  repeated Resource resources = 1;
  int64 total = 2;
}

message GetResourceRequest {
  string request_id = 1;
  string app_code = 2;
  int64 resource_id = 3;
}

message GetResourceResponse {
  Resource resource = 1;
}

message CreateResourceRequest {
  string request_id = 1;
  string app_code = 2;
  string resource_code = 3;
  string resource_type = 4;
  string name = 5;
  string status = 6;
  bool grantable = 7;
  string grant_strategy = 8;
  string wallet_asset_type = 9;
  int64 wallet_asset_amount = 10;
  repeated string usage_scopes = 11;
  string asset_url = 12;
  string preview_url = 13;
  string animation_url = 14;
  string metadata_json = 15;
  int32 sort_order = 16;
  int64 operator_user_id = 17;
  optional bool manager_grant_enabled = 18;
  string price_type = 19;
  int64 coin_price = 20;
  // gift_point_amount 是历史字段；服务端忽略请求值并固定写入 0。
  int64 gift_point_amount = 21;
}

message UpdateResourceRequest {
  string request_id = 1;
  string app_code = 2;
  int64 resource_id = 3;
  string resource_code = 4;
  string resource_type = 5;
  string name = 6;
  string status = 7;
  bool grantable = 8;
  string grant_strategy = 9;
  string wallet_asset_type = 10;
  int64 wallet_asset_amount = 11;
  repeated string usage_scopes = 12;
  string asset_url = 13;
  string preview_url = 14;
  string animation_url = 15;
  string metadata_json = 16;
  int32 sort_order = 17;
  int64 operator_user_id = 18;
  optional bool manager_grant_enabled = 19;
  string price_type = 20;
  int64 coin_price = 21;
  // gift_point_amount 是历史字段；服务端忽略请求值并固定写入 0。
  int64 gift_point_amount = 22;
}

message SetResourceStatusRequest {
  string request_id = 1;
  string app_code = 2;
  int64 resource_id = 3;
  string status = 4;
  int64 operator_user_id = 5;
}

message DeleteResourceRequest {
  string request_id = 1;
  string app_code = 2;
  int64 resource_id = 3;
  int64 operator_user_id = 4;
}

message BatchDeleteResourcesRequest {
  string request_id = 1;
  string app_code = 2;
  repeated int64 resource_ids = 3;
  int64 operator_user_id = 4;
}

message BatchDeleteResourcesResponse {
  repeated Resource resources = 1;
}

message ResourceResponse {
  Resource resource = 1;
}

message ListResourceGroupsRequest {
  string request_id = 1;
  string app_code = 2;
  string status = 3;
  string keyword = 4;
  int32 page = 5;
  int32 page_size = 6;
  bool active_only = 7;
}

message ListResourceGroupsResponse {
  repeated ResourceGroup groups = 1;
  int64 total = 2;
}

message GetResourceGroupRequest {
  string request_id = 1;
  string app_code = 2;
  int64 group_id = 3;
}

message GetResourceGroupResponse {
  ResourceGroup group = 1;
  // 对 group、ordered items 和嵌套 resource 的规范化 SHA-256。
  string source_content_hash = 2;
}

message CreateResourceGroupRequest {
  string request_id = 1;
  string app_code = 2;
  string group_code = 3;
  string name = 4;
  string status = 5;
  string description = 6;
  int32 sort_order = 7;
  repeated ResourceGroupItemInput items = 8;
  int64 operator_user_id = 9;
}

message UpdateResourceGroupRequest {
  string request_id = 1;
  string app_code = 2;
  int64 group_id = 3;
  string group_code = 4;
  string name = 5;
  string status = 6;
  string description = 7;
  int32 sort_order = 8;
  repeated ResourceGroupItemInput items = 9;
  int64 operator_user_id = 10;
}

message SetResourceGroupStatusRequest {
  string request_id = 1;
  string app_code = 2;
  int64 group_id = 3;
  string status = 4;
  int64 operator_user_id = 5;
}

message ResourceGroupResponse {
  ResourceGroup group = 1;
}

message ListGiftConfigsRequest {
  string request_id = 1;
  string app_code = 2;
  string status = 3;
  string keyword = 4;
  int32 page = 5;
  int32 page_size = 6;
  bool active_only = 7;
  int64 region_id = 8;
  bool filter_region = 9;
  string gift_type_code = 10;
}

message ListGiftConfigsResponse {
  repeated GiftConfig gifts = 1;
  int64 total = 2;
}

message ListGiftTypeConfigsRequest {
  string request_id = 1;
  string app_code = 2;
  string status = 3;
  bool active_only = 4;
}

message ListGiftTypeConfigsResponse {
  repeated GiftTypeConfig gift_types = 1;
}

message UpsertGiftTypeConfigRequest {
  string request_id = 1;
  string app_code = 2;
  string type_code = 3;
  string name = 4;
  string tab_key = 5;
  string status = 6;
  int32 sort_order = 7;
  int64 operator_user_id = 8;
}

message GiftTypeConfigResponse {
  GiftTypeConfig gift_type = 1;
}

message CreateGiftConfigRequest {
  string request_id = 1;
  string app_code = 2;
  string gift_id = 3;
  int64 resource_id = 4;
  string status = 5;
  string name = 6;
  int32 sort_order = 7;
  string presentation_json = 8;
  string price_version = 9;
  int64 coin_price = 10;
  // gift_point_amount 是历史字段；服务端忽略请求值并固定写入 0。
  int64 gift_point_amount = 11;
  int64 heat_value = 12;
  int64 effective_at_ms = 13;
  int64 operator_user_id = 14;
  repeated int64 region_ids = 15;
  string gift_type_code = 16;
  string charge_asset_type = 17;
  int64 effective_from_ms = 18;
  int64 effective_to_ms = 19;
  repeated string effect_types = 20;
  string cp_relation_type = 21;
  // vip_level 仅允许 VIP/贵族礼物填写，且必须对应当前 App 已配置的 VIP 等级。
  int32 vip_level = 22;
}

message BatchCreateGiftConfigsRequest {
  string request_id = 1;
  string app_code = 2;
  repeated CreateGiftConfigRequest items = 3;
  int64 operator_user_id = 4;
}

message BatchCreateGiftConfigsResponse {
  repeated GiftConfig gifts = 1;
}

message UpdateGiftConfigRequest {
  string request_id = 1;
  string app_code = 2;
  string gift_id = 3;
  int64 resource_id = 4;
  string status = 5;
  string name = 6;
  int32 sort_order = 7;
  string presentation_json = 8;
  string price_version = 9;
  int64 coin_price = 10;
  // gift_point_amount 是历史字段；服务端忽略请求值并固定写入 0。
  int64 gift_point_amount = 11;
  int64 heat_value = 12;
  int64 effective_at_ms = 13;
  int64 operator_user_id = 14;
  repeated int64 region_ids = 15;
  string gift_type_code = 16;
  string charge_asset_type = 17;
  int64 effective_from_ms = 18;
  int64 effective_to_ms = 19;
  repeated string effect_types = 20;
  string cp_relation_type = 21;
  // vip_level 仅允许 VIP/贵族礼物填写，且必须对应当前 App 已配置的 VIP 等级。
  int32 vip_level = 22;
}

message SetGiftConfigStatusRequest {
  string request_id = 1;
  string app_code = 2;
  string gift_id = 3;
  string status = 4;
  int64 operator_user_id = 5;
}

message DeleteGiftConfigRequest {
  string request_id = 1;
  string app_code = 2;
  string gift_id = 3;
  int64 operator_user_id = 4;
}

message GiftConfigResponse {
  GiftConfig gift = 1;
}

message GrantResourceRequest {
  string command_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  int64 resource_id = 4;
  int64 quantity = 5;
  int64 duration_ms = 6;
  string reason = 7;
  int64 operator_user_id = 8;
  string grant_source = 9;
}

message GrantResourceGroupRequest {
  string command_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  int64 group_id = 4;
  string reason = 5;
  int64 operator_user_id = 6;
  string grant_source = 7;
}

message GrantPinnedResourceGroupRequest {
  string command_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  string snapshot_id = 4;
  string reason = 5;
  int64 operator_user_id = 6;
  string grant_source = 7;
  // 发布版本持有的快照哈希；wallet 必须与 snapshot_id 对应的 owner 快照精确匹配。
  string expected_snapshot_hash = 8;
}

message RevokeResourceGrantRequest {
  string request_id = 1;
  string app_code = 2;
  string grant_id = 3;
  string reason = 4;
  int64 operator_user_id = 5;
}

message ResourceGrantResponse {
  ResourceGrant grant = 1;
}

message ListUserResourcesRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  string resource_type = 4;
  bool active_only = 5;
}

message ListUserResourcesResponse {
  repeated UserResourceEntitlement resources = 1;
}

message EquipUserResourceRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  int64 resource_id = 4;
  string entitlement_id = 5;
  // command_id 是装备状态变更的业务幂等键；request_id 只用于链路追踪。
  string command_id = 6;
}

message EquipUserResourceResponse {
  UserResourceEntitlement resource = 1;
}

message UnequipUserResourceRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  string resource_type = 4;
}

message UnequipUserResourceResponse {
  string resource_type = 1;
  bool unequipped = 2;
  int64 updated_at_ms = 3;
}

message BatchGetUserEquippedResourcesRequest {
  string request_id = 1;
  string app_code = 2;
  repeated int64 user_ids = 3;
  repeated string resource_types = 4;
  // vip_benefit_codes 只在调用方需要批量装配展示权限时传入；wallet 会同时校验
  // 当前 effective VIP、精确等级权益、体验卡可用性和用户开关，避免调用方逐用户 N+1 校验。
  repeated string vip_benefit_codes = 5;
}

message UserEquippedResources {
  int64 user_id = 1;
  repeated UserResourceEntitlement resources = 2;
  // allowed_vip_benefit_codes 只返回请求中最终可执行的权益；设置值本身不能越权进入此集合。
  repeated string allowed_vip_benefit_codes = 3;
  // effective_vip_level 在请求 vip_benefit_codes 时与权益判定共享同一付费/体验卡快照；
  // 未请求时为 0，调用方不得再按资源反推等级。
  int32 effective_vip_level = 4;
}

message BatchGetUserEquippedResourcesResponse {
  repeated UserEquippedResources users = 1;
}

message ListResourceGrantsRequest {
  string request_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  string status = 4;
  int32 page = 5;
  int32 page_size = 6;
  // operator_user_ids 由可信后台入口注入；外管历史同时包含当前 linked App user
  // 和旧版本 synthetic external operator，不能依赖前端传值做授权过滤。
  repeated int64 operator_user_ids = 7;
  // grant_source 与 operator_user_ids 共同限定发放入口，避免同一用户的活动奖励
  // 或主后台发放混入外管个人历史。
  string grant_source = 8;
}

message ListResourceGrantsResponse {
  repeated ResourceGrant grants = 1;
  int64 total = 2;
}

message ResourceShopItemInput {
  int64 shop_item_id = 1;
  int64 resource_id = 2;
  string status = 3;
  int32 duration_days = 4;
  int64 effective_from_ms = 5;
  int64 effective_to_ms = 6;
  int32 sort_order = 7;
  // coin_price 是当前售卖规格自己的金币价；新建传 0 使用资源列表价，更新已有规格传 0 保留当前售价。
  int64 coin_price = 8;
}

message ListResourceShopItemsRequest {
  string request_id = 1;
  string app_code = 2;
  string resource_type = 3;
  string status = 4;
  string keyword = 5;
  int32 page = 6;
  int32 page_size = 7;
  bool active_only = 8;
}

message ListResourceShopItemsResponse {
  repeated ResourceShopItem items = 1;
  int64 total = 2;
}

message UpsertResourceShopItemsRequest {
  string request_id = 1;
  string app_code = 2;
  repeated ResourceShopItemInput items = 3;
  int64 operator_user_id = 4;
}

message UpsertResourceShopItemsResponse {
  repeated ResourceShopItem items = 1;
}

message SetResourceShopItemStatusRequest {
  string request_id = 1;
  string app_code = 2;
  int64 shop_item_id = 3;
  string status = 4;
  int64 operator_user_id = 5;
}

message ResourceShopItemResponse {
  ResourceShopItem item = 1;
}

message PurchaseResourceShopItemRequest {
  string command_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  int64 shop_item_id = 4;
}

message PurchaseResourceShopItemResponse {
  string order_id = 1;
  string transaction_id = 2;
  string resource_grant_id = 3;
  ResourceShopItem shop_item = 4;
  UserResourceEntitlement resource = 5;
  AssetBalance balance = 6;
  int64 coin_spent = 7;
}

message RechargeBill {
  string app_code = 1;
  string transaction_id = 2;
  string command_id = 3;
  string recharge_type = 4;
  string status = 5;
  string external_ref = 6;
  int64 user_id = 7;
  int64 seller_user_id = 8;
  int64 seller_region_id = 9;
  int64 target_region_id = 10;
  int64 policy_id = 11;
  string policy_version = 12;
  string currency_code = 13;
  int64 coin_amount = 14;
  int64 usd_minor_amount = 15;
  int64 exchange_coin_amount = 16;
  int64 exchange_usd_minor_amount = 17;
  int64 created_at_ms = 18;
  int64 provider_amount_minor = 19;
  // provider_code 标识账单实际支付渠道：google_play/mifapay/v5pay/usdt_trc20。
  string provider_code = 20;
  // user_paid_* 是用户实付事实：外部订单来自三方下单快照，Google 订单来自 Play Orders API。
  string user_paid_currency_code = 21;
  int64 user_paid_amount_micro = 22;
  // provider_fee/tax/net 是三方扣款事实（实付币种微单位）；0 表示渠道未提供该数据。
  int64 provider_fee_micro = 23;
  int64 provider_tax_micro = 24;
  int64 provider_net_micro = 25;
  // paid_synced_at_ms 仅对 Google 账单有意义：Orders API 实付明细的同步时间，0 表示未同步。
  int64 paid_synced_at_ms = 26;
}

message ListRechargeBillsRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  int64 seller_user_id = 4;
  int64 region_id = 5;
  string recharge_type = 6;
  string status = 7;
  string keyword = 8;
  int64 start_at_ms = 9;
  int64 end_at_ms = 10;
  int32 page = 11;
  int32 page_size = 12;
  // paid_state=unsynced 只看实付明细未同步的谷歌账单（财务补数视图）。
  string paid_state = 13;
}

message ListRechargeBillsResponse {
  repeated RechargeBill bills = 1;
  int64 total = 2;
}

message GetRechargeBillSummaryRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  int64 seller_user_id = 4;
  int64 region_id = 5;
  string recharge_type = 6;
  string status = 7;
  string keyword = 8;
  int64 start_at_ms = 9;
  int64 end_at_ms = 10;
}

message RechargeBillSummaryBucket {
  int64 bill_count = 1;
  int64 coin_amount = 2;
  int64 usd_minor_amount = 3;
}

message GetRechargeBillSummaryResponse {
  RechargeBillSummaryBucket total = 1;
  RechargeBillSummaryBucket google_play = 2;
  RechargeBillSummaryBucket third_party = 3;
  // coin_seller 是币商进货口径：coin_seller_stock_records 里 counts_as_seller_recharge 的进货与冲回净额。
  RechargeBillSummaryBucket coin_seller = 4;
}

// UserRechargeStats 是 wallet-service 已维护的用户累计充值聚合，不允许后台回扫充值流水临时计算。
message UserRechargeStats {
  int64 user_id = 1;
  int64 recharge_count = 2;
  int64 total_coin_amount = 3;
  int64 total_usd_minor_amount = 4;
  int64 first_recharged_at_ms = 5;
  int64 updated_at_ms = 6;
}

message BatchGetUserRechargeStatsRequest {
  string request_id = 1;
  string app_code = 2;
  repeated int64 user_ids = 3;
}

message BatchGetUserRechargeStatsResponse {
  repeated UserRechargeStats items = 1;
}

message GetRechargeBillOverviewRequest {
  string request_id = 1;
  string app_code = 2;
  int64 region_id = 3;
  string recharge_type = 4;
  string status = 5;
  int64 start_at_ms = 6;
  int64 end_at_ms = 7;
  // tz_offset_minutes 决定“日”边界（中国时区 480）；日趋势与前端展示同一时区口径。
  int32 tz_offset_minutes = 8;
}

message RechargeBillDailyBucket {
  // date 为 tz_offset_minutes 时区下的 YYYY-MM-DD。
  string date = 1;
  int64 google_usd_minor = 2;
  int64 third_party_usd_minor = 3;
  int64 coin_seller_usd_minor = 4;
  int64 google_coin_amount = 5;
  int64 third_party_coin_amount = 6;
  int64 coin_seller_coin_amount = 7;
}

message RechargeBillRegionBucket {
  int64 region_id = 1;
  int64 bill_count = 2;
  int64 usd_minor_amount = 3;
}

// RechargeBillGooglePaidStats 是谷歌实付同步覆盖度与扣费估算：
// est_* 只统计已同步且净收入有效的账单，按“美金流水 × 实付占比”折算成 USD 最小单位。
message RechargeBillGooglePaidStats {
  int64 google_bill_count = 1;
  int64 synced_count = 2;
  int64 unsynced_count = 3;
  int64 covered_usd_minor = 4;
  int64 est_fee_usd_minor = 5;
  int64 est_tax_usd_minor = 6;
  int64 est_net_usd_minor = 7;
}

// RechargeBillSalaryTransferStats 是“用户提现”口径中来自钱包账本的部分：
// 用户把工资 USDT 转给币商（salary_transfer_to_coin_seller）。
message RechargeBillSalaryTransferStats {
  int64 transfer_count = 1;
  int64 transfer_usd_minor = 2;
}

message GetRechargeBillOverviewResponse {
  repeated RechargeBillDailyBucket daily = 1;
  repeated RechargeBillRegionBucket regions = 2;
  RechargeBillGooglePaidStats google_paid = 3;
  RechargeBillSalaryTransferStats salary_transfer = 4;
}

message RefreshGooglePaymentPricesRequest {
  string request_id = 1;
  string app_code = 2;
  repeated string transaction_ids = 3;
}

message GooglePaymentPrice {
  string transaction_id = 1;
  string currency_code = 2;
  int64 paid_amount_micro = 3;
  int64 tax_micro = 4;
  int64 net_micro = 5;
  int64 synced_at_ms = 6;
  string error = 7;
}

message RefreshGooglePaymentPricesResponse {
  repeated GooglePaymentPrice prices = 1;
}

// WalletFeatureFlags 是我的页和钱包首页使用的操作开关，来源属于 wallet-service。
message WalletFeatureFlags {
  bool recharge_enabled = 1;
  bool diamond_exchange_enabled = 2;
}

message GetWalletOverviewRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
}

message GetWalletOverviewResponse {
  repeated AssetBalance balances = 1;
  WalletFeatureFlags feature_flags = 2;
}

// WalletValueSummary 是我的页钱包价值展示的轻量投影，只返回首屏展示需要的金币余额。
message WalletValueSummary {
  int64 coin_amount = 1;
  int64 updated_at_ms = 2;
}

message GetWalletValueSummaryRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
}

message GetWalletValueSummaryResponse {
  WalletValueSummary summary = 1;
}

// GiftWallItem 是用户礼物墙按礼物类型聚合后的当前投影。
message GiftWallItem {
  string gift_id = 1;
  string gift_name = 2;
  int64 resource_id = 3;
  Resource resource = 4;
  string resource_snapshot_json = 5;
  string gift_type_code = 6;
  string presentation_json = 7;
  int64 gift_count = 8;
  int64 total_value = 9;
  int64 total_coin_value = 10;
  int64 total_diamond_value = 11;
  // total_gift_point 是历史礼物墙字段；新送礼不再累加。
  int64 total_gift_point = 12;
  int64 total_heat_value = 13;
  string charge_asset_type = 14;
  string last_price_version = 15;
  int64 first_received_at_ms = 16;
  int64 last_received_at_ms = 17;
  int32 sort_order = 18;
}

message GetUserGiftWallRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
}

message GetUserGiftWallResponse {
  repeated GiftWallItem items = 1;
  int64 gift_kind_count = 2;
  int64 gift_total_count = 3;
  int64 total_value = 4;
  int64 total_coin_value = 5;
  int64 total_diamond_value = 6;
  // total_gift_point 是历史礼物墙汇总字段；新送礼不再累加。
  int64 total_gift_point = 7;
  int64 total_heat_value = 8;
}

// RechargeProduct 是 App 充值页可展示的区域化充值档位。
message RechargeProduct {
  int64 product_id = 1;
  string product_code = 2;
  string channel = 3;
  string currency_code = 4;
  int64 coin_amount = 5;
  int64 amount_minor = 6;
  string policy_version = 7;
  bool enabled = 8;
  int32 sort_order = 9;
  string product_name = 10;
  string description = 11;
  string platform = 12;
  repeated int64 region_ids = 13;
  string resource_asset_type = 14;
  int64 amount_micro = 15;
  int64 created_by_user_id = 16;
  int64 updated_by_user_id = 17;
  int64 created_at_ms = 18;
  int64 updated_at_ms = 19;
  string app_code = 20;
  string status = 21;
  // audience_type 区分 H5 普通用户档位和币商档位；旧 App 内购默认 normal。
  string audience_type = 22;
  // app_visible 只控制 App 充值列表展示；active 但不可见的 Google SKU 仍可用于服务端验单和历史订单补偿。
  bool app_visible = 23;
}

message ListRechargeProductsRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  int64 region_id = 4;
  // platform 来自 gateway 解析的 App 平台请求头；为空时返回该区域全部已上架商品。
  string platform = 5;
  // audience_type 只用于 H5 站外充值；App 旧入口为空时按 normal 兼容。
  string audience_type = 6;
}

message ListRechargeProductsResponse {
  repeated RechargeProduct products = 1;
  repeated string channels = 2;
}

message ConfirmGooglePaymentRequest {
  string request_id = 1;
  string app_code = 2;
  string command_id = 3;
  int64 user_id = 4;
  int64 region_id = 5;
  int64 product_id = 6;
  string product_code = 7;
  string package_name = 8;
  string purchase_token = 9;
  string order_id = 10;
  int64 purchase_time_ms = 11;
  // country_id 由 gateway 从 user-service 当前用户资料注入，用于充值事实按真实国家进入 BI。
  int64 country_id = 12;
}

message ConfirmGooglePaymentResponse {
  string payment_order_id = 1;
  string transaction_id = 2;
  string status = 3;
  int64 product_id = 4;
  string product_code = 5;
  int64 coin_amount = 6;
  AssetBalance balance = 7;
  bool idempotent_replay = 8;
  string consume_state = 9;
}

// ListAdminRechargeProductsRequest 是后台内购配置列表查询，不参与用户充值主链路。
message ListAdminRechargeProductsRequest {
  string request_id = 1;
  string app_code = 2;
  string status = 3;
  string platform = 4;
  int64 region_id = 5;
  string keyword = 6;
  int32 page = 7;
  int32 page_size = 8;
  string audience_type = 9;
}

message ListAdminRechargeProductsResponse {
  repeated RechargeProduct products = 1;
  int64 total = 2;
}

// CreateRechargeProductRequest 配置一个 App 内购商品；首版资源资产固定为 COIN。
message CreateRechargeProductRequest {
  string request_id = 1;
  string app_code = 2;
  int64 amount_micro = 3;
  int64 coin_amount = 4;
  string product_name = 5;
  string description = 6;
  string platform = 7;
  repeated int64 region_ids = 8;
  bool enabled = 9;
  int64 operator_user_id = 10;
  string audience_type = 11;
  // 不传时创建为可见，兼容旧后台；传 false 可保留 Google 验单能力但从 App 普通充值列表隐藏。
  optional bool app_visible = 12;
}

message UpdateRechargeProductRequest {
  string request_id = 1;
  string app_code = 2;
  int64 product_id = 3;
  int64 amount_micro = 4;
  int64 coin_amount = 5;
  string product_name = 6;
  string description = 7;
  string platform = 8;
  repeated int64 region_ids = 9;
  bool enabled = 10;
  int64 operator_user_id = 11;
  string audience_type = 12;
  // 不传时保留原可见性，避免旧后台编辑隐藏 SKU 时意外改变展示面。
  optional bool app_visible = 13;
}

message DeleteRechargeProductRequest {
  string request_id = 1;
  string app_code = 2;
  int64 product_id = 3;
  int64 operator_user_id = 4;
}

message RechargeProductResponse {
  RechargeProduct product = 1;
}

message DeleteRechargeProductResponse {
  bool deleted = 1;
}

// ThirdPartyPaymentMethod 是后台和 H5 共用的三方支付方式配置快照。
message ThirdPartyPaymentMethod {
  int64 method_id = 1;
  string app_code = 2;
  string provider_code = 3;
  string provider_name = 4;
  string country_code = 5;
  string country_name = 6;
  string currency_code = 7;
  string pay_way = 8;
  string pay_type = 9;
  string method_name = 10;
  string logo_url = 11;
  string status = 12;
  string usd_to_currency_rate = 13;
  int32 sort_order = 14;
  int64 created_at_ms = 15;
  int64 updated_at_ms = 16;
}

message ThirdPartyPaymentChannel {
  string app_code = 1;
  string provider_code = 2;
  string provider_name = 3;
  string status = 4;
  int32 sort_order = 5;
  repeated ThirdPartyPaymentMethod methods = 6;
}

message ListThirdPartyPaymentChannelsRequest {
  string request_id = 1;
  string app_code = 2;
  string provider_code = 3;
  string status = 4;
  bool include_disabled_methods = 5;
}

message ListThirdPartyPaymentChannelsResponse {
  repeated ThirdPartyPaymentChannel channels = 1;
}

message SetThirdPartyPaymentMethodStatusRequest {
  string request_id = 1;
  string app_code = 2;
  int64 method_id = 3;
  bool enabled = 4;
  int64 operator_user_id = 5;
}

message ThirdPartyPaymentMethodResponse {
  ThirdPartyPaymentMethod method = 1;
}

message UpdateThirdPartyPaymentRateRequest {
  string request_id = 1;
  string app_code = 2;
  int64 method_id = 3;
  string usd_to_currency_rate = 4;
  int64 operator_user_id = 5;
}

message ThirdPartyPaymentMethodSyncIssue {
  string country_code = 1;
  string currency_code = 2;
  string code = 3;
  string message = 4;
}

message SyncThirdPartyPaymentMethodsRequest {
  string request_id = 1;
  string app_code = 2;
  string provider_code = 3;
  int64 operator_user_id = 4;
}

message SyncThirdPartyPaymentMethodsResponse {
  string provider_code = 1;
  int32 scanned_country_count = 2;
  int32 fetched_country_count = 3;
  int32 created_count = 4;
  int32 updated_count = 5;
  int32 skipped_count = 6;
  repeated ThirdPartyPaymentMethodSyncIssue issues = 7;
}

message H5RechargeOptionsRequest {
  string request_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  int64 target_region_id = 4;
  string target_country_code = 5;
  string audience_type = 6;
}

message H5RechargeOptionsResponse {
  repeated RechargeProduct products = 1;
  repeated ThirdPartyPaymentMethod payment_methods = 2;
  bool usdt_trc20_enabled = 3;
  string usdt_trc20_address = 4;
}

message ExternalRechargeOrder {
  string order_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  int64 target_region_id = 4;
  string target_country_code = 5;
  string audience_type = 6;
  int64 product_id = 7;
  string product_name = 8;
  int64 coin_amount = 9;
  int64 usd_minor_amount = 10;
  string provider_code = 11;
  int64 payment_method_id = 12;
  string country_code = 13;
  string currency_code = 14;
  int64 provider_amount_minor = 15;
  string pay_way = 16;
  string pay_type = 17;
  string pay_url = 18;
  string provider_order_id = 19;
  string tx_hash = 20;
  string receive_address = 21;
  string status = 22;
  string failure_reason = 23;
  string transaction_id = 24;
  int64 created_at_ms = 25;
  int64 updated_at_ms = 26;
  bool idempotent_replay = 27;
  // target_country_id 是下单时目标用户国家快照，支付回调入账直接复用，避免异步回调再查用户主数据。
  int64 target_country_id = 28;
}

message CreateH5RechargeOrderRequest {
  string request_id = 1;
  string app_code = 2;
  string command_id = 3;
  int64 target_user_id = 4;
  int64 target_region_id = 5;
  string target_country_code = 6;
  string audience_type = 7;
  int64 product_id = 8;
  string provider_code = 9;
  int64 payment_method_id = 10;
  string return_url = 11;
  string notify_url = 12;
  string client_ip = 13;
  string language = 14;
  // payer_name / payer_account 来自 gateway 重新解析的目标用户资料，不接受 H5 前端自报。
  string payer_name = 15;
  string payer_account = 16;
  string payer_email = 17;
  // target_country_id 由 gateway 从 user-service 解析目标用户，不接受 H5 前端自报。
  int64 target_country_id = 18;
}

message CreateTemporaryRechargeOrderRequest {
  string request_id = 1;
  string app_code = 2;
  string command_id = 3;
  int64 coin_amount = 4;
  int64 usd_minor_amount = 5;
  string provider_code = 6;
  int64 payment_method_id = 7;
  string return_url = 8;
  string notify_url = 9;
  string client_ip = 10;
  string language = 11;
  string payer_name = 12;
  string payer_account = 13;
  string payer_email = 14;
}

message GetTemporaryRechargeOrderRequest {
  string request_id = 1;
  string app_code = 2;
  string order_id = 3;
}

message ListTemporaryRechargeOrdersRequest {
  string request_id = 1;
  string app_code = 2;
  string status = 3;
  string provider_code = 4;
  string keyword = 5;
  int32 page = 6;
  int32 page_size = 7;
}

message ListTemporaryRechargeOrdersResponse {
  repeated ExternalRechargeOrder orders = 1;
  int64 total = 2;
}

message SubmitH5RechargeTxRequest {
  string request_id = 1;
  string app_code = 2;
  string order_id = 3;
  int64 target_user_id = 4;
  string tx_hash = 5;
}

message GetH5RechargeOrderRequest {
  string request_id = 1;
  string app_code = 2;
  string order_id = 3;
  int64 target_user_id = 4;
}

message H5RechargeOrderResponse {
  ExternalRechargeOrder order = 1;
}

message VerifyCoinSellerRechargeReceiptRequest {
  string request_id = 1;
  string app_code = 2;
  string provider_code = 3;
  string external_order_no = 4;
  string chain = 5;
  int64 usd_minor_amount = 6;
  int64 order_date_ms = 7;
  string provider_country_code = 8;
  string provider_currency_code = 9;
  string provider_pay_type = 10;
  int64 provider_amount_minor = 11;
  // receive_address 由可信后台从 app-scoped SQL 配置读取；非空时覆盖 wallet YAML，
  // 让运营修改收款地址后立即按新地址校验，同时避免 wallet-service 反向直连 admin 数据库。
  string receive_address = 12;
}

message VerifyCoinSellerRechargeReceiptResponse {
  bool verified = 1;
  string provider_code = 2;
  string external_order_no = 3;
  string provider_order_id = 4;
  string status = 5;
  string currency_code = 6;
  int64 provider_amount_minor = 7;
  string raw_json = 8;
  string chain = 9;
  string receive_address = 10;
  int64 verified_at_ms = 11;
  string failure_reason = 12;
}

message HandleMifapayNotifyRequest {
  string request_id = 1;
  string app_code = 2;
  string mer_account = 3;
  string data = 4;
  string sign = 5;
}

message HandleMifapayNotifyResponse {
  bool accepted = 1;
  string response_text = 2;
  string order_id = 3;
}

message HandleV5PayNotifyRequest {
  string request_id = 1;
  string app_code = 2;
  string raw_json = 3;
  map<string, string> fields = 4;
}

message HandleV5PayNotifyResponse {
  bool accepted = 1;
  string response_text = 2;
  string order_id = 3;
}

message DiamondExchangeRule {
  string exchange_type = 1;
  string from_asset_type = 2;
  string to_asset_type = 3;
  int64 from_amount = 4;
  int64 to_amount = 5;
  bool enabled = 6;
}

message GetDiamondExchangeConfigRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
}

message GetDiamondExchangeConfigResponse {
  repeated DiamondExchangeRule rules = 1;
}

// ExchangePointToCoin 把产品文案中的“钻石”POINT 兑换到本人 COIN；
// 客户端只提交源数量，region/now 由 gateway 从登录用户与服务端时间注入，交易在事务内重新解析已发布政策。
message ExchangePointToCoinRequest {
  string command_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  int64 point_amount = 4;
  int64 region_id = 5;
  int64 now_ms = 6;
  // 为空保持历史 POINT；永久钻石积分入口固定传 POINT_DIAMOND。
  string source_asset_type = 7;
}

message ExchangePointToCoinResponse {
  string transaction_id = 1;
  int64 point_amount = 2;
  int64 coin_amount = 3;
  int64 point_balance_after = 4;
  int64 coin_balance_after = 5;
  int64 ratio_point_amount = 6;
  int64 ratio_coin_amount = 7;
  string source_asset_type = 8;
  // POINT_DIAMOND 固化首次兑换所用 Host 政策快照；历史 POINT 返回 0。
  uint64 policy_id = 9;
  uint64 policy_version = 10;
}

// WalletTransaction 是用户钱包流水页使用的分录级投影。
message WalletTransaction {
  int64 entry_id = 1;
  string transaction_id = 2;
  string biz_type = 3;
  string asset_type = 4;
  int64 available_delta = 5;
  int64 frozen_delta = 6;
  int64 available_after = 7;
  int64 frozen_after = 8;
  int64 counterparty_user_id = 9;
  string room_id = 10;
  int64 created_at_ms = 11;
  int64 transfer_usd_minor = 12;
  string transfer_currency_code = 13;
  // entry_role 是面向客户端的分录语义；同一 gift_debit 下可区分 Host 基础收入和 Agency 分成。
  string entry_role = 14;
  // 同人既是 Host 又是 Agency 时账户只写一笔增量，但这两个字段仍保留各自的礼物收益组成。
  int64 host_base_amount = 15;
  int64 agency_amount = 16;
  uint64 policy_id = 17;
  uint64 policy_version = 18;
  int64 ratio_point_amount = 19;
  int64 ratio_coin_amount = 20;
  int64 gross_usd_minor = 21;
  int64 fee_usd_minor = 22;
  int64 net_usd_minor = 23;
  int64 withdraw_fee_bps = 24;
}

message ListWalletTransactionsRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  string asset_type = 4;
  int32 page = 5;
  int32 page_size = 6;
  // 时间范围统一使用 UTC epoch milliseconds 半开区间 [start_at_ms, end_at_ms)。
  int64 start_at_ms = 7;
  int64 end_at_ms = 8;
}

message ListWalletTransactionsResponse {
  repeated WalletTransaction transactions = 1;
  int64 total = 2;
}

message VipRewardItem {
  int64 resource_id = 1;
  string resource_code = 2;
  string resource_type = 3;
  string name = 4;
  int64 quantity = 5;
  int64 expires_at_ms = 6;
  string asset_url = 7;
  string preview_url = 8;
  string animation_url = 9;
}

// VipProgramConfig 是按 App 隔离的 VIP 规则开关。业务服务只能依据这些显式策略执行，
// 禁止通过 app_code 分支推断升级有效期、权益继承或后台发放语义。
message VipProgramConfig {
  string app_code = 1;
  // program_type: legacy_timed/tiered_privilege_v1。
  string program_type = 2;
  int32 level_count = 3;
  // same_level_expiry_policy: extend_remaining；同级购买从当前有效截止时间续期。
  string same_level_expiry_policy = 4;
  // upgrade_expiry_policy: extend_remaining/replace_from_now。
  string upgrade_expiry_policy = 5;
  // downgrade_purchase_policy 首版固定 reject，保留字段用于后台完整展示当前规则。
  string downgrade_purchase_policy = 6;
  // benefit_inheritance_policy 当前固定 target_only；每级提交功能权益的最终完整集合。
  // 带 resource_id 的装扮素材由 CheckVipBenefit 按具体资源执行 level <= effective level 校验。
  string benefit_inheritance_policy = 7;
  // grant_mode: direct_membership/trial_card，决定后台“赠送 VIP”的真实账务对象。
  string grant_mode = 8;
  bool trial_card_enabled = 9;
  string status = 10;
  int64 config_version = 11;
  int64 updated_by_admin_id = 12;
  int64 created_at_ms = 13;
  int64 updated_at_ms = 14;
}

// VipBenefit 是服务端可执行的权益事实。resource_id 为 0 表示纯功能特权，非 0 时
// 指向钱包资源；trial_enabled=false 的权益不会进入体验卡产生的 effective_benefits。
message VipBenefit {
  string benefit_code = 1;
  string name = 2;
  // benefit_type: decoration/function。
  string benefit_type = 3;
  // unlock_level 只作为 P1 默认矩阵的来源信息；后台仍按每个等级提交完整权益集合，
  // 运行时不得据此自动补齐高等级权益。
  int32 unlock_level = 4;
  string status = 5;
  bool trial_enabled = 6;
  int64 resource_id = 7;
  string resource_type = 8;
  // execution_scope 标识 wallet/room/user/notice 等实际权限执行方，不代表服务间直连。
  string execution_scope = 9;
  bool auto_equip = 10;
  int32 sort_order = 11;
  string metadata_json = 12;
  int64 created_at_ms = 13;
  int64 updated_at_ms = 14;
  VipBenefitPresentation presentation = 15;
  // resource 仅在 resource_id 指向同 App、active 且 resource_type 匹配的真实资源时返回。
  // 运营尚未配置素材的权益保留 resource_id/resource_type 事实，但该对象为空，客户端不得猜 URL。
  Resource resource = 16;
}

message VipLevel {
  int32 level = 1;
  string name = 2;
  string status = 3;
  int64 price_coin = 4;
  int64 duration_ms = 5;
  int64 reward_resource_group_id = 6;
  repeated VipRewardItem reward_items = 7;
  bool can_purchase = 8;
  int32 sort_order = 9;
  bool recharge_gate_required = 10;
  int64 required_recharge_coin_amount = 11;
  int64 user_recharge_coin_amount = 12;
  string purchase_locked_reason = 13;
  int64 created_at_ms = 14;
  int64 updated_at_ms = 15;
  // benefits 是后台为该等级显式保存的最终权益集合，运行时不按 unlock_level 自动继承。
  repeated VipBenefit benefits = 16;
  int64 config_version = 17;
}

message UserVip {
  int64 user_id = 1;
  int32 level = 2;
  string name = 3;
  bool active = 4;
  int64 started_at_ms = 5;
  int64 expires_at_ms = 6;
  int64 updated_at_ms = 7;
  string program_type = 8;
  int64 config_version = 9;
}

// VipTrialCard 是体验卡背包权益的 VIP 投影。entitlement_id 复用通用背包佩戴事实，
// 切换卡片只改变装备记录，不修改任何卡片自身的 expires_at_ms。
message VipTrialCard {
  string trial_card_id = 1;
  string entitlement_id = 2;
  int64 resource_id = 3;
  int64 user_id = 4;
  int32 level = 5;
  string name = 6;
  string status = 7;
  bool equipped = 8;
  int64 duration_ms = 9;
  int64 effective_at_ms = 10;
  int64 expires_at_ms = 11;
  int64 remaining_duration_ms = 12;
  string grant_source = 13;
  string source_grant_id = 14;
  int64 created_at_ms = 15;
  int64 updated_at_ms = 16;
}

// VipUserSettings 是用户按 App 隔离的可关闭 VIP 功能偏好。没有持久记录时 wallet
// 默认开启进房/上线通知、关闭三项隐私能力，updated_at_ms=0；设置本身不授予任何 VIP 权益。
message VipUserSettings {
  string app_code = 1;
  int64 user_id = 2;
  bool room_entry_notice_enabled = 3;
  bool online_global_notice_enabled = 4;
  int64 updated_at_ms = 5;
  bool hide_profile_data_enabled = 6;
  bool anonymous_profile_visit_enabled = 7;
  bool leaderboard_invisible_enabled = 8;
}

// VipState 同时返回付费会员、当前佩戴体验卡和最终生效会员，避免调用方自行合并两层状态。
// effective_benefits 是钱包按 paid/trial 来源、该等级显式矩阵和 trial_enabled 过滤后的资格事实；
// 对可关闭通知，执行方还必须结合 user_settings 或调用 CheckVipBenefit。
message VipState {
  UserVip paid_vip = 1;
  VipTrialCard equipped_trial_card = 2;
  UserVip effective_vip = 3;
  // effective_source: paid/trial/none。
  string effective_source = 4;
  repeated VipBenefit effective_benefits = 5;
  int64 evaluated_at_ms = 6;
  VipProgramConfig program_config = 7;
  VipUserSettings user_settings = 8;
}

message ListVipPackagesRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
}

message ListVipPackagesResponse {
  UserVip current_vip = 1;
  repeated VipLevel packages = 2;
  VipState state = 3;
  VipProgramConfig program_config = 4;
}

message GetMyVipRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
}

message GetMyVipResponse {
  UserVip vip = 1;
  VipState state = 2;
}

// UserEffectiveVipLevel 是 wallet 对单个用户当前最终生效 VIP 的轻量投影。
// level=0/source=none 表示当前 App 下没有有效付费会员或已装备体验卡。
message UserEffectiveVipLevel {
  int64 user_id = 1;
  int32 level = 2;
  int64 expires_at_ms = 3;
  // source: paid/trial/none。
  string source = 4;
}

// BatchGetUserEffectiveVipLevelsRequest 服务会话资料等一页级展示聚合；批次上限为 100。
message BatchGetUserEffectiveVipLevelsRequest {
  string request_id = 1;
  string app_code = 2;
  repeated int64 user_ids = 3;
}

message BatchGetUserEffectiveVipLevelsResponse {
  repeated UserEffectiveVipLevel users = 1;
  int64 evaluated_at_ms = 2;
}

message PurchaseVipRequest {
  string command_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  int32 level = 4;
}

message PurchaseVipResponse {
  string order_id = 1;
  string transaction_id = 2;
  UserVip vip = 3;
  int64 coin_spent = 4;
  int64 coin_balance_after = 5;
  repeated VipRewardItem reward_items = 6;
  VipState state = 7;
  // coin_balance 是购买事务提交后的带版本余额快照；coin_balance_after 仅保留旧客户端兼容。
  AssetBalance coin_balance = 8;
}

message DebitCPBreakupFeeRequest {
  string command_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  string relationship_id = 4;
  string relation_type = 5;
  int64 amount = 6;
}

message DebitCPBreakupFeeResponse {
  string transaction_id = 1;
  int64 coin_spent = 2;
  int64 coin_balance_after = 3;
  AssetBalance balance = 4;
}

message DebitWheelDrawRequest {
  string command_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  string wheel_id = 4;
  int32 draw_count = 5;
  int64 amount = 6;
}

message DebitWheelDrawResponse {
  string transaction_id = 1;
  int64 coin_spent = 2;
  int64 coin_balance_after = 3;
  AssetBalance balance = 4;
}

message GrantVipRequest {
  string command_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  int32 level = 4;
  string grant_source = 5;
  int64 operator_user_id = 6;
  string reason = 7;
}

message GrantVipResponse {
  string transaction_id = 1;
  UserVip vip = 2;
  repeated VipRewardItem reward_items = 3;
  int64 server_time_ms = 4;
  VipState state = 5;
}

message GetVipProgramConfigRequest {
  string request_id = 1;
  string app_code = 2;
}

message GetVipProgramConfigResponse {
  VipProgramConfig config = 1;
  repeated VipBenefit benefits = 2;
  int64 server_time_ms = 3;
}

// UpdateAdminVipProgramConfigRequest 以完整配置替换单 App 当前规则；config_version 由服务端递增，
// 客户端提交的审计时间和操作者字段不会被信任。
message UpdateAdminVipProgramConfigRequest {
  string request_id = 1;
  string app_code = 2;
  VipProgramConfig config = 3;
  int64 operator_user_id = 4;
}

message UpdateAdminVipProgramConfigResponse {
  VipProgramConfig config = 1;
  int64 server_time_ms = 2;
}

// UpdateMyVipSettingsRequest 使用 optional 保留 PATCH 语义；显式 false 不能被当成未提交。
message UpdateMyVipSettingsRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  optional bool room_entry_notice_enabled = 4;
  optional bool online_global_notice_enabled = 5;
  optional bool hide_profile_data_enabled = 6;
  optional bool anonymous_profile_visit_enabled = 7;
  optional bool leaderboard_invisible_enabled = 8;
}

message UpdateMyVipSettingsResponse {
  VipUserSettings settings = 1;
  int64 server_time_ms = 2;
}

// VipDailyCoinRebateRun 固化某个 UTC 自然日的返现金额矩阵。批任务开始后即使后台
// 修改等级权益，后续分页也只能复用本 run 的 config_version/level_amounts_json。
message VipDailyCoinRebateRun {
  string run_id = 1;
  string task_day = 2;
  int64 day_start_ms = 3;
  int64 day_end_ms = 4;
  int64 config_version = 5;
  string level_amounts_json = 6;
  string status = 7;
  int64 last_user_id = 8;
  int64 scanned_count = 9;
  int64 created_count = 10;
  int64 created_at_ms = 11;
  int64 updated_at_ms = 12;
  int64 completed_at_ms = 13;
}

// VipDailyCoinRebate 是付费 VIP 在 UTC 日切时生成的可领取金币事实。体验卡不会生成；
// status 返回 claimable/claimed/expired，expired 可由查询时钟投影而不改写历史行。
message VipDailyCoinRebate {
  string rebate_id = 1;
  int64 user_id = 2;
  string task_day = 3;
  int32 vip_level = 4;
  string vip_name = 5;
  int64 coin_amount = 6;
  string status = 7;
  int64 available_at_ms = 8;
  int64 expires_at_ms = 9;
  int64 config_version = 10;
  int64 claimed_at_ms = 11;
  string wallet_transaction_id = 12;
  int64 created_at_ms = 13;
  int64 updated_at_ms = 14;
}

// ProcessVipDailyCoinRebateBatchRequest 由 WalletCronService 接收。task_day 为空时按
// wallet-service 当前 UTC 日期处理；同 App+task_day 始终创建或复用同一个 run。
message ProcessVipDailyCoinRebateBatchRequest {
  string request_id = 1;
  string app_code = 2;
  string task_day = 3;
  int32 batch_size = 4;
}

message ProcessVipDailyCoinRebateBatchResponse {
  VipDailyCoinRebateRun run = 1;
  int32 scanned_count = 2;
  int32 created_count = 3;
  bool has_more = 4;
  int64 server_time_ms = 5;
}

message GetMyCurrentVipDailyCoinRebateRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
}

message GetMyCurrentVipDailyCoinRebateResponse {
  bool found = 1;
  VipDailyCoinRebate rebate = 2;
  int64 server_time_ms = 3;
}

message ListMyVipDailyCoinRebateStatusesRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  int32 page = 4;
  int32 page_size = 5;
  // rebate_ids 非空时精确恢复指定通知的领取状态，最多 100 个；仍强制按 user_id 隔离。
  repeated string rebate_ids = 6;
}

message ListMyVipDailyCoinRebateStatusesResponse {
  repeated VipDailyCoinRebate rebates = 1;
  int64 total = 2;
  int64 server_time_ms = 3;
}

message ClaimVipDailyCoinRebateRequest {
  string command_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  string rebate_id = 4;
}

message ClaimVipDailyCoinRebateResponse {
  VipDailyCoinRebate rebate = 1;
  string transaction_id = 2;
  AssetBalance coin_balance = 3;
  int64 server_time_ms = 4;
}

// GrantVipTrialCardRequest 允许后台直接按 level+duration_ms 发卡；resource_id 为空时按
// vip_trial_card_{level} 解析同 App 预置资源，非空时也必须匹配该等级。
message GrantVipTrialCardRequest {
  string command_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  int32 level = 4;
  int64 duration_ms = 5;
  optional int64 resource_id = 6;
  string grant_source = 7;
  int64 operator_user_id = 8;
  string reason = 9;
}

message GrantVipTrialCardResponse {
  VipTrialCard trial_card = 1;
  VipState state = 2;
  int64 server_time_ms = 3;
}

message EquipVipTrialCardRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  // entitlement_id 是背包实例标识，保证同资源的多张卡也能被精确佩戴。
  string entitlement_id = 4;
}

message EquipVipTrialCardResponse {
  VipTrialCard trial_card = 1;
  VipState state = 2;
  int64 server_time_ms = 3;
}

message UnequipVipTrialCardRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
}

message UnequipVipTrialCardResponse {
  bool unequipped = 1;
  VipState state = 2;
  int64 server_time_ms = 3;
}

// CheckVipBenefit 供 room/user 等权益执行方校验单个特权。调用方不能自行比较 VIP level，
// 因为体验卡排除项、资源目录状态和 App 配置会改变最终可执行权益集合。
message CheckVipBenefitRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  string benefit_code = 4;
  // resource_id/resource_type 同时传入时校验具体装扮资源。资源型权益允许使用当前
  // effective VIP 等级及以下仍处于 active 目录的绑定；不传时保持功能权益的精确等级语义。
  int64 resource_id = 5;
  string resource_type = 6;
}

message CheckVipBenefitResponse {
  bool allowed = 1;
  VipBenefit benefit = 2;
  VipState state = 3;
  string denial_reason = 4;
  int64 evaluated_at_ms = 5;
  // required_level 是当前 App 所有 active 等级中首次显式配置该权益的等级；0 表示未配置。
  int32 required_level = 6;
  // current_effective_level 来自 wallet 合并付费会员与体验卡后的最终状态。
  int32 current_effective_level = 7;
}

message AdminVipLevelInput {
  int32 level = 1;
  string name = 2;
  string status = 3;
  int64 price_coin = 4;
  int64 duration_ms = 5;
  int64 reward_resource_group_id = 6;
  int32 sort_order = 7;
  int64 required_recharge_coin_amount = 8;
  // benefits 是该等级完整、显式的权益集合；服务端不根据 unlock_level 自动继承。
  repeated VipBenefit benefits = 9;
}

message ListAdminVipLevelsRequest {
  string request_id = 1;
  string app_code = 2;
}

message ListAdminVipLevelsResponse {
  repeated VipLevel levels = 1;
  int64 server_time_ms = 2;
  VipProgramConfig program_config = 3;
}

message UpdateAdminVipLevelsRequest {
  string request_id = 1;
  string app_code = 2;
  repeated AdminVipLevelInput levels = 3;
  int64 operator_user_id = 4;
}

message UpdateAdminVipLevelsResponse {
  repeated VipLevel levels = 1;
  int64 server_time_ms = 2;
  VipProgramConfig program_config = 3;
}

// CreditTaskRewardRequest 是 activity-service 领取任务奖励时调用的钱包入账命令。
message CreditTaskRewardRequest {
  string command_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  int64 amount = 4;
  string task_type = 5;
  string task_id = 6;
  string cycle_key = 7;
  string reason = 8;
  string reward_asset_type = 9;
}

// CreditTaskRewardResponse 返回任务奖励入账流水和用户奖励资产账后余额。
message CreditTaskRewardResponse {
  string transaction_id = 1;
  AssetBalance balance = 2;
  int64 amount = 3;
  int64 granted_at_ms = 4;
}

// CreditLuckyGiftRewardRequest 是 activity-service 抽奖 outbox 补偿 worker 调用的钱包入账命令。
message CreditLuckyGiftRewardRequest {
  string command_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  int64 amount = 4;
  string draw_id = 5;
  string room_id = 6;
  string gift_id = 7;
  string pool_id = 8;
  string reason = 9;
  int64 visible_region_id = 10;
  // country_id 是中奖用户所属国家，钱包 outbox 原样发布给统计服务，避免返奖只落到区域未知国家。
  int64 country_id = 11;
}

// CreditLuckyGiftRewardResponse 返回幸运礼物返奖入账流水和用户 COIN 账后余额。
message CreditLuckyGiftRewardResponse {
  string transaction_id = 1;
  AssetBalance balance = 2;
  int64 amount = 3;
  int64 granted_at_ms = 4;
}

// CreditWheelRewardRequest 是 activity-service 转盘金币奖品 outbox 调用的钱包入账命令。
message CreditWheelRewardRequest {
  string command_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  int64 amount = 4;
  string draw_id = 5;
  string wheel_id = 6;
  string selected_tier_id = 7;
  string reason = 8;
  int64 visible_region_id = 9;
}

// CreditWheelRewardResponse 返回转盘金币奖品入账流水和用户 COIN 账后余额。
message CreditWheelRewardResponse {
  string transaction_id = 1;
  AssetBalance balance = 2;
  int64 amount = 3;
  int64 granted_at_ms = 4;
}

// CreditRoomTurnoverRewardRequest 是 activity-service 每周房间流水奖励结算后的金币入账命令。
message CreditRoomTurnoverRewardRequest {
  string command_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  int64 amount = 4;
  string settlement_id = 5;
  string room_id = 6;
  int64 period_start_ms = 7;
  int64 period_end_ms = 8;
  int64 coin_spent = 9;
  int64 tier_id = 10;
  string tier_code = 11;
  string reason = 12;
}

// CreditRoomTurnoverRewardResponse 返回房间流水奖励入账流水和用户 COIN 账后余额。
message CreditRoomTurnoverRewardResponse {
  string transaction_id = 1;
  AssetBalance balance = 2;
  int64 amount = 3;
  int64 granted_at_ms = 4;
}

// CreditInviteActivityRewardRequest 是 activity-service 邀请活动达标后发起的 COIN 奖励入账命令。
message CreditInviteActivityRewardRequest {
  string command_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  int64 amount = 4;
  string claim_id = 5;
  string reward_type = 6;
  int64 tier_id = 7;
  string tier_code = 8;
  string cycle_key = 9;
  int64 reached_value = 10;
  string reason = 11;
}

// CreditInviteActivityRewardResponse 返回邀请活动奖励入账流水和用户 COIN 账后余额。
message CreditInviteActivityRewardResponse {
  string transaction_id = 1;
  AssetBalance balance = 2;
  int64 amount = 3;
  int64 granted_at_ms = 4;
}

// CreditAgencyOpeningRewardRequest 是 activity-service 代理开业流水档位结算后的 COIN 入账命令。
message CreditAgencyOpeningRewardRequest {
  string command_id = 1;
  string app_code = 2;
  int64 target_user_id = 3;
  int64 amount = 4;
  string application_id = 5;
  string cycle_id = 6;
  int64 agency_id = 7;
  int32 rank_no = 8;
  int64 score_coins = 9;
  string reason = 10;
}

// CreditAgencyOpeningRewardResponse 返回代理开业奖励入账流水和用户 COIN 账后余额。
message CreditAgencyOpeningRewardResponse {
  string transaction_id = 1;
  AssetBalance balance = 2;
  int64 amount = 3;
  int64 granted_at_ms = 4;
}

// ApplyGameCoinChangeRequest 是 game-service 唯一可以调用的钱包游戏改账入口。
message ApplyGameCoinChangeRequest {
  string request_id = 1;
  string app_code = 2;
  string command_id = 3;
  int64 user_id = 4;
  string platform_code = 5;
  string game_id = 6;
  string provider_order_id = 7;
  string provider_round_id = 8;
  string op_type = 9;
  int64 coin_amount = 10;
  string room_id = 11;
  string request_hash = 12;
}

// ApplyGameCoinChangeResponse 返回游戏改账流水和用户 COIN 账后余额。
message ApplyGameCoinChangeResponse {
  string wallet_transaction_id = 1;
  int64 balance_after = 2;
  bool idempotent_replay = 3;
}

message RedPacketConfig {
  string app_code = 1;
  bool enabled = 2;
  repeated int32 count_tiers = 3;
  repeated int64 amount_tiers = 4;
  int32 delayed_open_seconds = 5;
  int32 expire_seconds = 6;
  int32 daily_send_limit = 7;
  int64 updated_by_admin_id = 8;
  int64 created_at_ms = 9;
  int64 updated_at_ms = 10;
  string rule_url = 11;
}

message RedPacketClaim {
  string app_code = 1;
  string claim_id = 2;
  string command_id = 3;
  string packet_id = 4;
  int64 user_id = 5;
  int64 amount = 6;
  string wallet_transaction_id = 7;
  string status = 8;
  string failure_reason = 9;
  int64 created_at_ms = 10;
  int64 updated_at_ms = 11;
}

message RedPacket {
  string app_code = 1;
  string packet_id = 2;
  string command_id = 3;
  int64 sender_user_id = 4;
  string room_id = 5;
  int64 region_id = 6;
  string packet_type = 7;
  int64 total_amount = 8;
  int32 packet_count = 9;
  int64 remaining_amount = 10;
  int32 remaining_count = 11;
  string status = 12;
  int64 open_at_ms = 13;
  int64 expires_at_ms = 14;
  int64 created_at_ms = 15;
  int64 updated_at_ms = 16;
  bool claimed_by_viewer = 17;
  int64 viewer_claim_amount = 18;
  repeated RedPacketClaim claims = 19;
  int64 refund_amount = 20;
  string refund_status = 21;
  int64 refunded_at_ms = 22;
}

message GetRedPacketConfigRequest {
  string request_id = 1;
  string app_code = 2;
}

message GetRedPacketConfigResponse {
  RedPacketConfig config = 1;
  int64 server_time_ms = 2;
}

message UpdateRedPacketConfigRequest {
  string request_id = 1;
  string app_code = 2;
  bool enabled = 3;
  repeated int32 count_tiers = 4;
  repeated int64 amount_tiers = 5;
  int32 delayed_open_seconds = 6;
  int32 expire_seconds = 7;
  int32 daily_send_limit = 8;
  int64 operator_user_id = 9;
  string rule_url = 10;
}

message UpdateRedPacketConfigResponse {
  RedPacketConfig config = 1;
  int64 server_time_ms = 2;
}

message CreateRedPacketRequest {
  string request_id = 1;
  string app_code = 2;
  string command_id = 3;
  int64 sender_user_id = 4;
  string room_id = 5;
  int64 region_id = 6;
  string packet_type = 7;
  int64 total_amount = 8;
  int32 packet_count = 9;
  // room_locked 由 gateway 从本次发红包前读取的 room-service snapshot 固化；wallet 只随资金事实透传。
  bool room_locked = 10;
  // owner_user_id 与 room_locked 来自同一份 room snapshot，只用于红包跨房入口的密码框交互。
  int64 owner_user_id = 11;
}

message CreateRedPacketResponse {
  RedPacket packet = 1;
  int64 balance_after = 2;
  int64 server_time_ms = 3;
}

message ClaimRedPacketRequest {
  string request_id = 1;
  string app_code = 2;
  string command_id = 3;
  string packet_id = 4;
  int64 user_id = 5;
}

message ClaimRedPacketResponse {
  RedPacketClaim claim = 1;
  int64 balance_after = 2;
  int64 server_time_ms = 3;
  RedPacket packet = 4;
}

message ListRedPacketsRequest {
  string request_id = 1;
  string app_code = 2;
  string room_id = 3;
  int64 sender_user_id = 4;
  int64 region_id = 5;
  string packet_type = 6;
  string status = 7;
  int64 viewer_user_id = 8;
  int64 start_at_ms = 9;
  int64 end_at_ms = 10;
  int32 page = 11;
  int32 page_size = 12;
}

message ListRedPacketsResponse {
  repeated RedPacket packets = 1;
  int64 total = 2;
  int64 server_time_ms = 3;
}

message GetRedPacketRequest {
  string request_id = 1;
  string app_code = 2;
  string packet_id = 3;
  int64 viewer_user_id = 4;
  bool include_claims = 5;
}

message GetRedPacketResponse {
  RedPacket packet = 1;
  int64 server_time_ms = 2;
}

message ExpireRedPacketsRequest {
  string request_id = 1;
  string app_code = 2;
  int32 batch_size = 3;
}

message ExpireRedPacketsResponse {
  int32 expired_count = 1;
  int64 refunded_amount = 2;
  int64 server_time_ms = 3;
}

message RetryRedPacketRefundRequest {
  string request_id = 1;
  string app_code = 2;
  string packet_id = 3;
  int64 operator_user_id = 4;
}

message RetryRedPacketRefundResponse {
  RedPacket packet = 1;
  int64 refunded_amount = 2;
  int64 server_time_ms = 3;
}

// CronBatchRequest 是 cron-service 触发 wallet-service 后台批处理的统一请求。
message CronBatchRequest {
  string request_id = 1;
  string app_code = 2;
  string run_id = 3;
  string worker_id = 4;
  int32 batch_size = 5;
  int64 lock_ttl_ms = 6;
  // trigger_mode 为空时由 wallet-service 按 automatic 处理；后台手动结算显式传 manual。
  string trigger_mode = 7;
  // cycle_key 为空时由 wallet-service 按当前 UTC 月处理；后台手动补结可指定周期。
  string cycle_key = 8;
  // user_ids 为空表示批处理扫描全部候选；后台手动结算只传已勾选主播。
  repeated int64 user_ids = 9;
  // settlement_role 为空时结算完整 Host/Agency 权益；后台人工入口可传 host 或 agency 拆分钱包。
  string settlement_role = 10;
}

// CronBatchResponse 返回一个批处理 run 的执行摘要。
message CronBatchResponse {
  int32 claimed_count = 1;
  int32 processed_count = 2;
  int32 success_count = 3;
  int32 failure_count = 4;
  bool has_more = 5;
}

message ResourceShopPurchaseOrder {
  string app_code = 1;
  string order_id = 2;
  string command_id = 3;
  int64 user_id = 4;
  int64 shop_item_id = 5;
  int64 resource_id = 6;
  Resource resource = 7;
  int32 duration_days = 8;
  int64 price_coin = 9;
  string status = 10;
  string wallet_transaction_id = 11;
  string resource_grant_id = 12;
  string entitlement_id = 13;
  int64 created_at_ms = 14;
  int64 updated_at_ms = 15;
}

message ListResourceShopPurchaseOrdersRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  string resource_type = 4;
  string status = 5;
  string keyword = 6;
  int32 page = 7;
  int32 page_size = 8;
}

message ListResourceShopPurchaseOrdersResponse {
  repeated ResourceShopPurchaseOrder orders = 1;
  int64 total = 2;
}

message GetGiftCatalogVersionRequest {
  string request_id = 1;
  string app_code = 2;
}

message GetGiftCatalogVersionResponse {
  int64 version = 1;
}

// HostRevenueStats 聚合主播在自然日区间内的收益 POINT 和送礼用户；gateway 对外仍按产品文案展示为钻石。
message HostRevenueStats {
  // 旧字段保持 SALARY_DIAMOND 收益与历史 POINT 转出，避免滚动升级改变 Lalu 口径。
  int64 diamond_earnings = 1;
  int64 diamond_exchanged = 2;
  int64 gift_senders = 3;
  int64 point_diamond_earnings = 4;
  int64 point_diamond_exchanged = 5;
}

message GetHostRevenueStatsRequest {
  string request_id = 1;
  string app_code = 2;
  int64 host_user_id = 3;
  int64 start_at_ms = 4;
  int64 end_at_ms = 5;
  int64 region_id = 6;
}

message GetHostRevenueStatsResponse {
  HostRevenueStats stats = 1;
}

message AgencyHostGiftStats {
  // 旧 gift_income 只表示 SALARY_DIAMOND Host 收益；永久积分收入不得混入。
  int64 gift_income = 1;
  int64 gifted_host_count = 2;
  int64 point_diamond_host_income = 4;
  int64 point_diamond_agency_share_income = 5;
}

message GetAgencyHostGiftStatsRequest {
  string request_id = 1;
  string app_code = 2;
  repeated int64 host_user_ids = 3;
  int64 start_at_ms = 4;
  int64 end_at_ms = 5;
  // Agency Center 传 owner user_id 以汇总 POINT_DIAMOND agency_amount；BD 视图保持 0。
  int64 agency_user_id = 6;
}

message GetAgencyHostGiftStatsResponse {
  AgencyHostGiftStats stats = 1;
  // 一次返回请求内所有主播的统计，避免 Agency Center 对 wallet-service 发起 N 次 RPC。
  repeated AgencyHostRevenueStats host_stats = 2;
}

message AgencyHostRevenueStats {
  int64 host_user_id = 1;
  HostRevenueStats stats = 2;
}

// PointWithdrawalCoinSellerConfig 是按 App 隔离的 POINT 提现币商白名单与兑换比例。
// 比例用整数分子/分母保存，避免浮点金额在 H5、gateway 和账本间产生舍入分歧。
message PointWithdrawalCoinSellerConfig {
  string app_code = 1;
  int64 seller_user_id = 2;
  int32 sort_order = 3;
  int64 point_amount = 4;
  int64 seller_coin_amount = 5;
  // service_country_codes 与 service_region_ids 互斥；两个数组都为空表示服务全部区域和国家。
  repeated string service_country_codes = 6;
  string status = 7;
  int64 created_at_ms = 8;
  int64 updated_at_ms = 9;
  repeated int64 service_region_ids = 10;
}

message ListPointWithdrawalCoinSellersRequest {
  string request_id = 1;
  string app_code = 2;
  // country_code 和 region_id 都由 gateway 从登录用户资料注入；空值只用于 Admin/内部查询。
  string country_code = 3;
  bool include_disabled = 4;
  int64 region_id = 5;
}

message ListPointWithdrawalCoinSellersResponse {
  repeated PointWithdrawalCoinSellerConfig sellers = 1;
}

message TransferPointToCoinSellerRequest {
  string command_id = 1;
  string app_code = 2;
  int64 source_user_id = 3;
  int64 seller_user_id = 4;
  int64 point_amount = 5;
  // source_country_code 由 gateway 从用户资料读取，wallet 会再次校验币商服务国家。
  string source_country_code = 6;
  string reason = 7;
  // region_id 只能由 gateway 从登录用户资料注入，用于解析当前 UTC 月工资政策。
  int64 region_id = 8;
  // 为空保持历史 POINT；POINT_DIAMOND 按 gross_usd_minor 和 Host 政策换算双方资产。
  string source_asset_type = 9;
  int64 gross_usd_minor = 10;
}

message TransferPointToCoinSellerResponse {
  string transaction_id = 1;
  int64 source_point_balance_after = 2;
  int64 seller_balance_after = 3;
  int64 point_amount = 4;
  int64 seller_coin_amount = 5;
  int64 ratio_point_amount = 6;
  int64 ratio_seller_coin_amount = 7;
  string source_asset_type = 8;
  int64 gross_usd_minor = 9;
  int64 fee_usd_minor = 10;
  int64 net_usd_minor = 11;
  int32 withdraw_fee_bps = 12;
  uint64 policy_id = 13;
  uint64 policy_version = 14;
}

message GetPointWithdrawalConfigRequest {
  string request_id = 1;
  string app_code = 2;
  int64 region_id = 3;
  int64 now_ms = 4;
  string asset_type = 5;
  // overview 传登录 user_id，以便 wallet 按当前 UTC 周期返回真实次数余量。
  int64 user_id = 6;
}

message PointWithdrawalActionAvailability {
  bool allowed = 1;
  string limit_period = 2;
  // limit_count=0、remaining_count=-1 表示不限次数。
  int64 limit_count = 3;
  int64 used_count = 4;
  int64 remaining_count = 5;
  string allowed_days = 6;
  string block_reason = 7;
}

message GetPointWithdrawalConfigResponse {
  bool found = 1;
  int64 points_per_usd = 2;
  int32 fee_bps = 3;
  int64 minimum_points = 4;
  string policy_instance_code = 5;
  // coins_per_usd 复用工资兑换普通金币的统一比例，与 points_per_usd 组合得到 POINT->COIN 展示比例。
  int64 coins_per_usd = 6;
  string policy_type = 7;
  int64 minimum_withdraw_usd_minor = 8;
  int32 agency_point_share_bps = 9;
  uint64 policy_id = 10;
  uint64 policy_version = 11;
  bool availability_evaluated = 12;
  PointWithdrawalActionAvailability coin_seller_availability = 13;
  PointWithdrawalActionAvailability platform_availability = 14;
  int64 coin_seller_minimum_withdraw_usd_minor = 15;
  int64 platform_minimum_withdraw_usd_minor = 16;
  // optional 区分旧 wallet-service 未返回字段与运营明确关闭，支持滚动升级时保持原开启行为。
  optional bool diamond_exchange_enabled = 17;
}

// VipBenefitPreviewItem 是同一权益下的一项预览内容；独立稳定键允许 Flutter 在多图、动态资源间切换，
// 且不会把数组下标误当成资源身份。
message VipBenefitPreviewItem {
  string preview_id = 1;
  string title = 2;
  // media_type: image/animation；animation 使用 animation_url，静态兜底使用 preview_url/asset_url。
  string media_type = 3;
  string asset_url = 4;
  string preview_url = 5;
  string animation_url = 6;
  // composition_type: none/user_avatar_center/user_avatar_waveform，客户端据此把当前用户头像合成到预览中。
  string composition_type = 7;
  int32 sort_order = 8;
}

message VipBenefitNumericReward {
  string label = 1;
  int64 value = 2;
  string unit = 3;
  // period: once/daily/monthly，由服务端配置返回，客户端只展示不据此结算。
  string period = 4;
}

// VipBenefitPresentation 是 Flutter 可直接渲染的结构化特权说明。metadata_json 继续保留后台扩展和
// 历史兼容，但客户端不得按 benefit_code 自行解析 metadata_json 生成预览。
message VipBenefitPresentation {
  string description = 1;
  repeated VipBenefitPreviewItem preview_items = 2;
  VipBenefitNumericReward numeric_reward = 3;
}

// RevokeUserResourceRequest targets one entitlement instead of its source grant. This keeps
// resource-group siblings intact when an operator removes a single badge/frame from a user.
message RevokeUserResourceRequest {
  string request_id = 1;
  string app_code = 2;
  int64 user_id = 3;
  string entitlement_id = 4;
  string reason = 5;
  int64 operator_user_id = 6;
}

message RevokeUserResourceResponse {
  UserResourceEntitlement resource = 1;
}

message GetResourceGrantRequest {
  string request_id = 1;
  string app_code = 2;
  string grant_id = 3;
}

// WalletCronService 只给 cron-service 调用；账务状态仍由 wallet-service owner 修改。
service WalletCronService {
  rpc ProcessHostSalaryDailySettlementBatch(CronBatchRequest) returns (CronBatchResponse);
  rpc ProcessHostSalaryHalfMonthSettlementBatch(CronBatchRequest) returns (CronBatchResponse);
  rpc ProcessHostSalaryMonthEndBatch(CronBatchRequest) returns (CronBatchResponse);
  rpc ProcessVipDailyCoinRebateBatch(ProcessVipDailyCoinRebateBatchRequest) returns (ProcessVipDailyCoinRebateBatchResponse);
}

// WalletService 是内部账务 gRPC 边界，外部 HTTP 入口仍由 gateway-service 承载。
service WalletService {
  rpc DebitGift(DebitGiftRequest) returns (DebitGiftResponse);
  rpc DebitDirectGift(DebitDirectGiftRequest) returns (DebitGiftResponse);
  rpc BatchDebitGift(BatchDebitGiftRequest) returns (BatchDebitGiftResponse);
  rpc DebitRobotGift(DebitRobotGiftRequest) returns (DebitGiftResponse);
  rpc GetBalances(GetBalancesRequest) returns (GetBalancesResponse);
  rpc GetActiveHostSalaryPolicy(GetActiveHostSalaryPolicyRequest) returns (GetActiveHostSalaryPolicyResponse);
  rpc GetHostSalaryProgress(GetHostSalaryProgressRequest) returns (GetHostSalaryProgressResponse);
  rpc GetHostRevenueStats(GetHostRevenueStatsRequest) returns (GetHostRevenueStatsResponse);
  rpc GetAgencyHostGiftStats(GetAgencyHostGiftStatsRequest) returns (GetAgencyHostGiftStatsResponse);
  // GetTeamHostSalaryStats 按 Agency 收款人集合聚合主播预收入工资；经理中心团队工资卡片使用。
  rpc GetTeamHostSalaryStats(GetTeamHostSalaryStatsRequest) returns (GetTeamHostSalaryStatsResponse);
  rpc AdminCreditAsset(AdminCreditAssetRequest) returns (AdminCreditAssetResponse);
  rpc AdminCreditCoinSellerStock(AdminCreditCoinSellerStockRequest) returns (AdminCreditCoinSellerStockResponse);
  rpc TransferCoinFromSeller(TransferCoinFromSellerRequest) returns (TransferCoinFromSellerResponse);
  rpc TransferCoinSellerStockToChild(TransferCoinSellerStockToChildRequest) returns (TransferCoinSellerStockToChildResponse);
  rpc ListCoinSellerSalaryExchangeRateTiers(ListCoinSellerSalaryExchangeRateTiersRequest) returns (ListCoinSellerSalaryExchangeRateTiersResponse);
  rpc ExchangeSalaryToCoin(ExchangeSalaryToCoinRequest) returns (ExchangeSalaryToCoinResponse);
  rpc TransferSalaryToCoinSeller(TransferSalaryToCoinSellerRequest) returns (TransferSalaryToCoinSellerResponse);
  rpc ListPointWithdrawalCoinSellers(ListPointWithdrawalCoinSellersRequest) returns (ListPointWithdrawalCoinSellersResponse);
  rpc TransferPointToCoinSeller(TransferPointToCoinSellerRequest) returns (TransferPointToCoinSellerResponse);
  rpc GetPointWithdrawalConfig(GetPointWithdrawalConfigRequest) returns (GetPointWithdrawalConfigResponse);
  rpc FreezeSalaryWithdrawal(FreezeSalaryWithdrawalRequest) returns (FreezeSalaryWithdrawalResponse);
  rpc SettleSalaryWithdrawal(SettleSalaryWithdrawalRequest) returns (SettleSalaryWithdrawalResponse);
  rpc ReleaseSalaryWithdrawal(ReleaseSalaryWithdrawalRequest) returns (ReleaseSalaryWithdrawalResponse);
  rpc FreezePointWithdrawal(FreezePointWithdrawalRequest) returns (FreezePointWithdrawalResponse);
  rpc SettlePointWithdrawal(SettlePointWithdrawalRequest) returns (SettlePointWithdrawalResponse);
  rpc ReleasePointWithdrawal(ReleasePointWithdrawalRequest) returns (ReleasePointWithdrawalResponse);
  rpc ListResources(ListResourcesRequest) returns (ListResourcesResponse);
  rpc GetResource(GetResourceRequest) returns (GetResourceResponse);
  rpc CreateResource(CreateResourceRequest) returns (ResourceResponse);
  rpc UpdateResource(UpdateResourceRequest) returns (ResourceResponse);
  rpc SetResourceStatus(SetResourceStatusRequest) returns (ResourceResponse);
  rpc DeleteResource(DeleteResourceRequest) returns (ResourceResponse);
  rpc BatchDeleteResources(BatchDeleteResourcesRequest) returns (BatchDeleteResourcesResponse);
  rpc ListResourceGroups(ListResourceGroupsRequest) returns (ListResourceGroupsResponse);
  rpc GetResourceGroup(GetResourceGroupRequest) returns (GetResourceGroupResponse);
  rpc CreateResourceGroup(CreateResourceGroupRequest) returns (ResourceGroupResponse);
  rpc UpdateResourceGroup(UpdateResourceGroupRequest) returns (ResourceGroupResponse);
  rpc SetResourceGroupStatus(SetResourceGroupStatusRequest) returns (ResourceGroupResponse);
  rpc PinResourceGroupSnapshot(PinResourceGroupSnapshotRequest) returns (PinResourceGroupSnapshotResponse);
  rpc ListGiftConfigs(ListGiftConfigsRequest) returns (ListGiftConfigsResponse);
  rpc ListGiftTypeConfigs(ListGiftTypeConfigsRequest) returns (ListGiftTypeConfigsResponse);
  rpc GetGiftCatalogVersion(GetGiftCatalogVersionRequest) returns (GetGiftCatalogVersionResponse);
  rpc CreateGiftConfig(CreateGiftConfigRequest) returns (GiftConfigResponse);
  rpc BatchCreateGiftConfigs(BatchCreateGiftConfigsRequest) returns (BatchCreateGiftConfigsResponse);
  rpc UpdateGiftConfig(UpdateGiftConfigRequest) returns (GiftConfigResponse);
  rpc SetGiftConfigStatus(SetGiftConfigStatusRequest) returns (GiftConfigResponse);
  rpc DeleteGiftConfig(DeleteGiftConfigRequest) returns (GiftConfigResponse);
  rpc UpsertGiftTypeConfig(UpsertGiftTypeConfigRequest) returns (GiftTypeConfigResponse);
  rpc GrantResource(GrantResourceRequest) returns (ResourceGrantResponse);
  rpc GrantResourceGroup(GrantResourceGroupRequest) returns (ResourceGrantResponse);
  rpc GrantPinnedResourceGroup(GrantPinnedResourceGroupRequest) returns (ResourceGrantResponse);
  // GetResourceGrant 按租户和精确 grant_id 读取发放快照，供异步活动消息复用真实发奖结果。
  rpc GetResourceGrant(GetResourceGrantRequest) returns (ResourceGrantResponse);
  rpc RevokeResourceGrant(RevokeResourceGrantRequest) returns (ResourceGrantResponse);
  rpc RevokeUserResource(RevokeUserResourceRequest) returns (RevokeUserResourceResponse);
  rpc ListUserResources(ListUserResourcesRequest) returns (ListUserResourcesResponse);
  rpc EquipUserResource(EquipUserResourceRequest) returns (EquipUserResourceResponse);
  rpc UnequipUserResource(UnequipUserResourceRequest) returns (UnequipUserResourceResponse);
  rpc BatchGetUserEquippedResources(BatchGetUserEquippedResourcesRequest) returns (BatchGetUserEquippedResourcesResponse);
  rpc ListResourceGrants(ListResourceGrantsRequest) returns (ListResourceGrantsResponse);
  rpc ListResourceShopItems(ListResourceShopItemsRequest) returns (ListResourceShopItemsResponse);
  rpc UpsertResourceShopItems(UpsertResourceShopItemsRequest) returns (UpsertResourceShopItemsResponse);
  rpc SetResourceShopItemStatus(SetResourceShopItemStatusRequest) returns (ResourceShopItemResponse);
  rpc ListResourceShopPurchaseOrders(ListResourceShopPurchaseOrdersRequest) returns (ListResourceShopPurchaseOrdersResponse);
  rpc PurchaseResourceShopItem(PurchaseResourceShopItemRequest) returns (PurchaseResourceShopItemResponse);
  rpc ListRechargeBills(ListRechargeBillsRequest) returns (ListRechargeBillsResponse);
  rpc GetRechargeBillSummary(GetRechargeBillSummaryRequest) returns (GetRechargeBillSummaryResponse);
  rpc BatchGetUserRechargeStats(BatchGetUserRechargeStatsRequest) returns (BatchGetUserRechargeStatsResponse);
  rpc GetRechargeBillOverview(GetRechargeBillOverviewRequest) returns (GetRechargeBillOverviewResponse);
  rpc RefreshGooglePaymentPrices(RefreshGooglePaymentPricesRequest) returns (RefreshGooglePaymentPricesResponse);
  rpc GetWalletOverview(GetWalletOverviewRequest) returns (GetWalletOverviewResponse);
  rpc GetWalletValueSummary(GetWalletValueSummaryRequest) returns (GetWalletValueSummaryResponse);
  rpc GetUserGiftWall(GetUserGiftWallRequest) returns (GetUserGiftWallResponse);
  rpc ListRechargeProducts(ListRechargeProductsRequest) returns (ListRechargeProductsResponse);
  rpc ConfirmGooglePayment(ConfirmGooglePaymentRequest) returns (ConfirmGooglePaymentResponse);
  rpc ListAdminRechargeProducts(ListAdminRechargeProductsRequest) returns (ListAdminRechargeProductsResponse);
  rpc CreateRechargeProduct(CreateRechargeProductRequest) returns (RechargeProductResponse);
  rpc UpdateRechargeProduct(UpdateRechargeProductRequest) returns (RechargeProductResponse);
  rpc DeleteRechargeProduct(DeleteRechargeProductRequest) returns (DeleteRechargeProductResponse);
  rpc ListThirdPartyPaymentChannels(ListThirdPartyPaymentChannelsRequest) returns (ListThirdPartyPaymentChannelsResponse);
  rpc SetThirdPartyPaymentMethodStatus(SetThirdPartyPaymentMethodStatusRequest) returns (ThirdPartyPaymentMethodResponse);
  rpc UpdateThirdPartyPaymentRate(UpdateThirdPartyPaymentRateRequest) returns (ThirdPartyPaymentMethodResponse);
  rpc SyncThirdPartyPaymentMethods(SyncThirdPartyPaymentMethodsRequest) returns (SyncThirdPartyPaymentMethodsResponse);
  rpc ListH5RechargeOptions(H5RechargeOptionsRequest) returns (H5RechargeOptionsResponse);
  rpc CreateH5RechargeOrder(CreateH5RechargeOrderRequest) returns (H5RechargeOrderResponse);
  rpc CreateTemporaryRechargeOrder(CreateTemporaryRechargeOrderRequest) returns (H5RechargeOrderResponse);
  rpc GetTemporaryRechargeOrder(GetTemporaryRechargeOrderRequest) returns (H5RechargeOrderResponse);
  rpc ListTemporaryRechargeOrders(ListTemporaryRechargeOrdersRequest) returns (ListTemporaryRechargeOrdersResponse);
  rpc SubmitH5RechargeTx(SubmitH5RechargeTxRequest) returns (H5RechargeOrderResponse);
  rpc GetH5RechargeOrder(GetH5RechargeOrderRequest) returns (H5RechargeOrderResponse);
  rpc VerifyCoinSellerRechargeReceipt(VerifyCoinSellerRechargeReceiptRequest) returns (VerifyCoinSellerRechargeReceiptResponse);
  rpc HandleMifapayNotify(HandleMifapayNotifyRequest) returns (HandleMifapayNotifyResponse);
  rpc HandleV5PayNotify(HandleV5PayNotifyRequest) returns (HandleV5PayNotifyResponse);
  rpc GetDiamondExchangeConfig(GetDiamondExchangeConfigRequest) returns (GetDiamondExchangeConfigResponse);
  rpc ExchangePointToCoin(ExchangePointToCoinRequest) returns (ExchangePointToCoinResponse);
  rpc ListWalletTransactions(ListWalletTransactionsRequest) returns (ListWalletTransactionsResponse);
  // 主播周期钻石是礼物收入投影，不属于通用可消费钱包资产，因此单独暴露查询语义。
  rpc ListHostPeriodDiamondTransactions(ListWalletTransactionsRequest) returns (ListWalletTransactionsResponse);
  rpc ListVipPackages(ListVipPackagesRequest) returns (ListVipPackagesResponse);
  rpc GetVipProgramConfig(GetVipProgramConfigRequest) returns (GetVipProgramConfigResponse);
  rpc GetMyVip(GetMyVipRequest) returns (GetMyVipResponse);
  rpc BatchGetUserEffectiveVipLevels(BatchGetUserEffectiveVipLevelsRequest) returns (BatchGetUserEffectiveVipLevelsResponse);
  rpc CheckVipBenefit(CheckVipBenefitRequest) returns (CheckVipBenefitResponse);
  rpc PurchaseVip(PurchaseVipRequest) returns (PurchaseVipResponse);
  rpc DebitCPBreakupFee(DebitCPBreakupFeeRequest) returns (DebitCPBreakupFeeResponse);
  rpc DebitWheelDraw(DebitWheelDrawRequest) returns (DebitWheelDrawResponse);
  rpc GrantVip(GrantVipRequest) returns (GrantVipResponse);
  rpc GrantVipTrialCard(GrantVipTrialCardRequest) returns (GrantVipTrialCardResponse);
  rpc EquipVipTrialCard(EquipVipTrialCardRequest) returns (EquipVipTrialCardResponse);
  rpc UnequipVipTrialCard(UnequipVipTrialCardRequest) returns (UnequipVipTrialCardResponse);
  rpc ListAdminVipLevels(ListAdminVipLevelsRequest) returns (ListAdminVipLevelsResponse);
  rpc UpdateAdminVipLevels(UpdateAdminVipLevelsRequest) returns (UpdateAdminVipLevelsResponse);
  rpc UpdateAdminVipProgramConfig(UpdateAdminVipProgramConfigRequest) returns (UpdateAdminVipProgramConfigResponse);
  rpc UpdateMyVipSettings(UpdateMyVipSettingsRequest) returns (UpdateMyVipSettingsResponse);
  rpc GetMyCurrentVipDailyCoinRebate(GetMyCurrentVipDailyCoinRebateRequest) returns (GetMyCurrentVipDailyCoinRebateResponse);
  rpc ListMyVipDailyCoinRebateStatuses(ListMyVipDailyCoinRebateStatusesRequest) returns (ListMyVipDailyCoinRebateStatusesResponse);
  rpc ClaimVipDailyCoinRebate(ClaimVipDailyCoinRebateRequest) returns (ClaimVipDailyCoinRebateResponse);
  rpc CreditTaskReward(CreditTaskRewardRequest) returns (CreditTaskRewardResponse);
  rpc CreditLuckyGiftReward(CreditLuckyGiftRewardRequest) returns (CreditLuckyGiftRewardResponse);
  rpc CreditWheelReward(CreditWheelRewardRequest) returns (CreditWheelRewardResponse);
  rpc CreditRoomTurnoverReward(CreditRoomTurnoverRewardRequest) returns (CreditRoomTurnoverRewardResponse);
  rpc CreditInviteActivityReward(CreditInviteActivityRewardRequest) returns (CreditInviteActivityRewardResponse);
  rpc CreditAgencyOpeningReward(CreditAgencyOpeningRewardRequest) returns (CreditAgencyOpeningRewardResponse);
  rpc ApplyGameCoinChange(ApplyGameCoinChangeRequest) returns (ApplyGameCoinChangeResponse);
  rpc GetRedPacketConfig(GetRedPacketConfigRequest) returns (GetRedPacketConfigResponse);
  rpc UpdateRedPacketConfig(UpdateRedPacketConfigRequest) returns (UpdateRedPacketConfigResponse);
  rpc CreateRedPacket(CreateRedPacketRequest) returns (CreateRedPacketResponse);
  rpc ClaimRedPacket(ClaimRedPacketRequest) returns (ClaimRedPacketResponse);
  rpc ListRedPackets(ListRedPacketsRequest) returns (ListRedPacketsResponse);
  rpc GetRedPacket(GetRedPacketRequest) returns (GetRedPacketResponse);
  rpc ExpireRedPackets(ExpireRedPacketsRequest) returns (ExpireRedPacketsResponse);
  rpc RetryRedPacketRefund(RetryRedPacketRefundRequest) returns (RetryRedPacketRefundResponse);
}
