npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@seaverse/assets-sdk

v0.0.2

Published

SeaVerse Assets API SDK - Unified multi-tenant payment and credit management service

Downloads

210

Readme

@seaverse/assets-sdk

SeaVerse Asset Hub SDK - 统一的多租户积分管理服务(4 池系统)

概述

Asset Hub SDK 提供了完整的积分管理功能,支持 4 池积分系统(daily/event/monthly/permanent)和传统的积分账户查询。所有应用程序(包括 SeaVerse 平台本身)都使用相同的 SDK 路由和多租户架构。

主要 API 端点:

  • /sdk/v1/credits/detail - 获取 4 池积分详情(推荐)
  • /sdk/v1/credits/account - 获取积分账户信息(已弃用)
  • /sdk/v1/credits/transactions - 查询交易历史

核心概念

应用 ID

每个应用程序都有唯一的 app_id:

  • SeaVerse 平台: appId = "seaverse" (官方平台应用,如 ops-frontend、meta-product)
  • 第三方应用: appId = "game-abc123"appId = "social-xyz789"

多租户隔离

  • 积分账户按 app_id 隔离
  • 每个应用都有自己的积分池和交易历史
  • 同一用户在不同应用中可以有不同的积分账户

安装

npm install @seaverse/assets-sdk
# 或
pnpm add @seaverse/assets-sdk

功能特性

  • 4 池积分系统: 支持 daily/event/monthly/permanent 四种积分池
  • 过期时间追踪: 每个积分池都有独立的过期时间戳
  • 消费优先级: Daily → Event → Monthly → Permanent
  • 查询用户积分账户信息,支持多租户
  • 列出交易历史,支持筛选和分页
  • TypeScript 支持,包含完整类型定义
  • 只读 API(积分消费/发放由平台后端处理)

快速开始

import { PaymentClient } from '@seaverse/assets-sdk';

// 为 SeaVerse 平台初始化客户端
const client = new PaymentClient({
  appId: 'seaverse',
  token: 'your-bearer-token'
});

// 或为第三方应用初始化
const gameClient = new PaymentClient({
  appId: 'game-abc123',
  token: 'your-bearer-token'
});

// 获取积分详情(推荐使用 - 4 池系统)
const detail = await client.getCreditDetail();
console.log(`总余额: ${detail.total_balance} 积分`);

// 显示各个积分池的详情
detail.pools.forEach(pool => {
  const expiryLabel = pool.expires_at === 0
    ? '永久有效'
    : `${Math.floor((pool.expires_at - Date.now()) / 86400000)} 天后过期`;
  console.log(`${pool.type}: ${pool.balance} (${expiryLabel})`);
});

// 列出最近的交易
const result = await client.listTransactions({ page_size: 10 });
console.log(`找到 ${result.transactions.length} 笔交易`);
console.log(`有更多: ${result.has_more}`);

4 池积分系统

Asset Hub SDK 使用 4 池积分系统,每种池有不同的过期规则和用途:

积分池类型

| 池类型 | 说明 | 过期规则 | |--------|------|----------| | daily | 每日积分 | 次日 00:00 UTC 过期 | | event | 活动积分 | 活动截止时间过期 | | monthly | 每月积分 | 最后发放时间 + 30天过期 | | permanent | 通用积分 | 永久有效 |

消费优先级

当用户消费积分时,系统按以下优先级扣除:

Daily (每日) → Event (活动) → Monthly (每月) → Permanent (通用)

这确保先消耗即将过期的积分,最大化用户的积分价值。

使用示例

const detail = await client.getCreditDetail();

// 计算总余额
console.log(`总计: ${detail.total_balance} 积分`);

// 遍历各个积分池
for (const pool of detail.pools) {
  const remaining = pool.expires_at === 0
    ? Infinity
    : pool.expires_at - Date.now();

  if (remaining === Infinity) {
    console.log(`${pool.type}: ${pool.balance} 积分 (永久有效)`);
  } else {
    const days = Math.floor(remaining / 86400000);
    const hours = Math.floor((remaining % 86400000) / 3600000);
    console.log(`${pool.type}: ${pool.balance} 积分 (${days}天${hours}小时后过期)`);
  }
}

// 检查是否有即将过期的积分
const expiringPools = detail.pools.filter(pool => {
  if (pool.expires_at === 0) return false;
  const daysLeft = (pool.expires_at - Date.now()) / 86400000;
  return daysLeft < 3; // 3天内过期
});

if (expiringPools.length > 0) {
  console.log('⚠️ 有积分即将过期,请尽快使用!');
  expiringPools.forEach(pool => {
    console.log(`  - ${pool.type}: ${pool.balance} 积分`);
  });
}

API 参考

PaymentClient

构造函数

new PaymentClient(options: PaymentClientOptions)

参数:

  • appId (string, 必需): 用于多租户隔离的应用 ID
    • 'seaverse' 用于 SeaVerse 平台
    • 'game-abc123' 用于第三方应用
  • token (string, 必需): 用于身份验证的 Bearer token
  • baseURL (string, 可选): Asset Hub API 的基础 URL
    • 默认: 'https://payment.sg.seaverse.dev'
    • 生产环境: 'https://asset.sg.seaverse.dev'
  • timeout (number, 可选): 请求超时时间(毫秒),默认 30000

方法

getCreditDetail()

获取积分详情(4 池系统)- 推荐使用

返回包含 4 种积分池的详细信息,每个池都有余额和过期时间戳。

API 端点: GET /sdk/v1/credits/detail

多租户行为:

  • 积分池属于 appId 指定的应用
  • 每个应用都有独立的 4 池系统
  • 池数据按 app_id 隔离

请求头:

  • Authorization: Bearer <token> - 必需
  • X-App-ID: <app_id> - 必需
async getCreditDetail(): Promise<CreditDetailResponse>

返回:

{
  total_balance: string           // 所有池的总余额(字符串保证精度)
  pools: [                        // 积分池数组(仅包含余额 > 0 的池)
    {
      type: 'daily' | 'event' | 'monthly' | 'permanent'
      balance: string             // 该池的余额(字符串保证精度)
      expires_at: number          // 过期时间戳(毫秒)
                                  // 0 = 永久有效
                                  // > 0 = 具体过期时间
    }
  ]
}

积分池类型:

  • daily - 每日积分(次日 00:00 UTC 过期)
  • event - 活动积分(活动截止时间过期)
  • monthly - 每月积分(最后发放时间 + 30天过期)
  • permanent - 通用积分(永久有效)

响应特点:

  • 仅返回 balance > 0 的积分池
  • expires_at 为毫秒时间戳(0 表示永久有效)
  • 前端可根据时间戳计算剩余时间

建议刷新频率: 30 秒

示例:

const detail = await client.getCreditDetail();
console.log(`总余额: ${detail.total_balance}`);

detail.pools.forEach(pool => {
  const label = pool.expires_at === 0
    ? '永久有效'
    : `${Math.floor((pool.expires_at - Date.now()) / 86400000)} 天后过期`;
  console.log(`${pool.type}: ${pool.balance} (${label})`);
});

// 输出示例:
// 总余额: 5240
// daily: 150 (0 天后过期)
// event: 500 (5 天后过期)
// monthly: 800 (28 天后过期)
// permanent: 3790 (永久有效)
getCreditAccount()

⚠️ 已弃用: 推荐使用 getCreditDetail() 来获取新的 4 池积分系统信息。

获取已认证用户在指定应用中的积分账户信息。

API 端点: GET /sdk/v1/credits/account

多租户行为:

  • 积分账户属于 appId 指定的应用
  • 每个应用都有隔离的积分池
  • 账户数据按 app_id 隔离

请求头:

  • Authorization: Bearer <token> - 必需
  • X-App-ID: <app_id> - 必需
async getCreditAccount(): Promise<CreditAccount>

返回:

{
  id: string
  user_id: string
  app_id: string | null           // 应用 ID (平台用户为 null)
  balance: number                  // 当前可用余额
  total_earned: number             // 累计获得
  total_spent: number              // 累计消费
  frozen_balance: number           // 冻结余额(不可用)
  last_transaction_id?: string
  status: 'active' | 'suspended' | 'frozen'
  status_reason?: string           // 仅在非活跃状态时存在
  created_at: string               // UTC 时间戳
  updated_at: string               // UTC 时间戳
  last_activity_at?: string        // UTC 时间戳
}

账户状态:

  • active - 正常,可用
  • suspended - 已暂停(请联系支持)
  • frozen - 已冻结(风险控制)

注意事项:

  • 某些字段可能为 nullundefined,请做好空值处理
  • 数字字段可能以字符串形式返回,SDK 会自动处理类型转换
  • 建议使用可选链操作符 ?. 访问可选字段

示例:

const account = await client.getCreditAccount();
console.log(`余额: ${account.balance ?? 0}`);
console.log(`应用: ${account.app_id ?? 'N/A'}`);
console.log(`状态: ${account.status}`);

// 安全访问可选字段
if (account.last_activity_at) {
  console.log(`最后活动: ${account.last_activity_at}`);
}
listTransactions()

列出指定应用的积分交易,支持筛选和分页。

API 端点: GET /sdk/v1/credits/transactions

多租户行为:

  • 交易按 appId 指定的应用筛选
  • 仅返回已认证用户在此应用中的交易
  • 交易历史按 app_id 隔离

请求头:

  • Authorization: Bearer <token> - 必需
  • X-App-ID: <app_id> - 必需
async listTransactions(request?: ListTransactionsRequest): Promise<TransactionListResponse>

请求参数:

  • type (可选): 按交易类型筛选
    • 'earn' - 积分获得
    • 'spend' - 积分扣除
    • 'freeze' - 冻结积分
    • 'unfreeze' - 解冻积分
    • 'refund' - 退款返还
    • 'adjust' - 管理员调整
  • source (可选): 按交易来源筛选(例如 'api_call''purchase')
  • status (可选): 按状态筛选
    • 'pending' - 处理中
    • 'completed' - 已完成
    • 'failed' - 失败
    • 'cancelled' - 已取消
  • start_date (可选): 开始日期筛选(RFC3339 格式,例如 '2024-01-01T00:00:00Z')
  • end_date (可选): 结束日期筛选(RFC3339 格式,例如 '2024-12-31T23:59:59Z')
  • page (可选): 页码(从 1 开始,默认: 1)
  • page_size (可选): 每页条数(最小: 1,最大: 50,默认: 20)

返回:

{
  transactions: CreditTransaction[]
  page: number
  page_size: number
  has_more: boolean
}

CreditTransaction:

{
  id: string
  user_id: string
  app_id: string | null              // 应用 ID (平台用户为 null)
  type: 'spend' | 'earn' | 'refund' | 'adjust' | 'freeze' | 'unfreeze'
  amount: number                     // 负数表示扣除,正数表示增加
  balance_before: number
  balance_after: number
  source: string                     // 交易来源标识符
  source_id?: string                 // 关联的来源 ID(订单/任务/请求 ID)
  description: string
  status: 'pending' | 'completed' | 'failed' | 'cancelled'
  status_reason?: string             // 仅在非已完成状态时存在
  metadata?: Record<string, any>     // 自定义元数据
  created_at: string                 // UTC 时间戳
  completed_at?: string              // UTC 时间戳
}

示例:

// 获取最近的交易
const result = await client.listTransactions({ page_size: 20 });

// 按类型筛选
const spending = await client.listTransactions({
  type: 'spend',
  page: 1,
  page_size: 10
});

// 按日期范围筛选
const recent = await client.listTransactions({
  start_date: '2024-01-01T00:00:00Z',
  end_date: '2024-12-31T23:59:59Z',
  status: 'completed'
});

// 分页
const page2 = await client.listTransactions({
  page: 2,
  page_size: 20
});
if (page2.has_more) {
  // 有更多结果可用
}
setToken()

更新用于身份验证的 bearer token。

当 token 过期或需要刷新时使用此方法。

setToken(token: string): void

示例:

client.setToken('new-bearer-token');
setBaseURL()

更新 API 的基础 URL。

使用此方法在不同环境(开发/测试/生产)之间切换。

setBaseURL(baseURL: string): void

示例:

client.setBaseURL('https://payment.sg.seaverse.dev');
setAppId()

更新用于多租户隔离的应用 ID。

使用此方法在运行时在不同应用之间切换。

setAppId(appId: string): void

示例:

// 切换到 SeaVerse 平台
client.setAppId('seaverse');

// 切换到第三方应用
client.setAppId('game-abc123');

错误处理

SDK 会为 API 错误抛出 PaymentAPIError:

import { PaymentAPIError } from '@seaverse/assets-sdk';

try {
  const account = await client.getCreditAccount();
} catch (error) {
  if (error instanceof PaymentAPIError) {
    console.error(`API 错误 [${error.code}]: ${error.message}`);
    console.error('响应:', error.response);
  } else {
    console.error('意外错误:', error);
  }
}

常见错误码:

  • 400 - 错误的请求(无效参数)
  • 401 - 未授权(无效或过期的 token)
  • 403 - 禁止访问
  • 500 - 内部服务器错误

环境配置

不同环境有不同的基础 URL:

// 开发/测试环境(默认)
const client = new PaymentClient({
  appId: 'seaverse',
  token: devToken
  // baseURL 默认为 'https://payment.sg.seaverse.dev'
});

// 生产环境(推荐)
const client = new PaymentClient({
  appId: 'seaverse',
  baseURL: 'https://asset.sg.seaverse.dev',
  token: prodToken
});

// 本地开发
const client = new PaymentClient({
  appId: 'seaverse',
  baseURL: 'http://localhost:8080',
  token: localToken
});

环境说明:

  • 开发/测试: https://payment.sg.seaverse.dev (SDK 默认)
  • 生产: https://asset.sg.seaverse.dev (推荐用于生产环境)
  • 本地: http://localhost:8080 (本地调试)

多租户使用示例

SeaVerse 平台应用

// 官方 SeaVerse 平台 (ops-frontend、meta-product 等)
const platformClient = new PaymentClient({
  appId: 'seaverse',
  token: userToken
});

const account = await platformClient.getCreditAccount();
console.log(`平台余额: ${account.balance}`);

第三方游戏应用

// 拥有自己积分池的第三方游戏
const gameClient = new PaymentClient({
  appId: 'game-abc123',
  token: userToken
});

const gameAccount = await gameClient.getCreditAccount();
console.log(`游戏余额: ${gameAccount.balance}`);

// 同一用户,不同应用 - 隔离的积分账户
// gameAccount.balance 可能与 platformClient 的余额不同

在应用之间切换

const client = new PaymentClient({
  appId: 'seaverse',
  token: userToken
});

// 查询 SeaVerse 平台账户
const platformAccount = await client.getCreditAccount();

// 切换到游戏应用
client.setAppId('game-abc123');

// 查询同一用户的游戏账户
const gameAccount = await client.getCreditAccount();

// 两个账户都属于同一用户,但是隔离的

积分单位

  • 1 积分 = 1 单位
  • 所有金额都表示为整数(无小数)

身份验证

  • Token 通过 SeaVerse auth-service 获取
  • Authorization header 中需要 Bearer token
  • 默认 token 有效期: 10 天
  • 所需范围: account

安全性

域名白名单(推荐)

对于生产应用,注册允许的域名以防止未经授权的使用:

  • Origin header 会根据应用的白名单进行验证
  • 防止 app_id 被盗用和滥用

速率限制

  • app_id 应用速率限制
  • 防止滥用

故障排除

常见问题

1. Cannot read properties of undefined 错误

如果遇到类似 Cannot read properties of undefined (reading 'toLocaleString') 的错误,说明 API 返回的某些字段为 undefinednull

解决方案:

// ✅ 推荐:使用空值合并运算符
const balance = account.balance ?? 0;
const totalEarned = account.total_earned ?? 0;

// ✅ 推荐:使用可选链
const lastActivity = account.last_activity_at ?? 'N/A';

// ✅ 推荐:显示前检查值是否存在
if (account.balance !== null && account.balance !== undefined) {
  console.log(`余额: ${account.balance.toLocaleString()}`);
}

2. CORS 错误

如果在浏览器中遇到 CORS 错误,请确保:

  • 你的域名已添加到应用的白名单中
  • X-App-ID header 正确设置
  • 使用正确的 baseURL

3. 401 未授权错误

可能原因:

  • Token 已过期(默认有效期 10 天)
  • Token 格式不正确
  • Token 没有正确的 scope(需要 account scope)

解决方案:

// 重新获取 token
const newToken = await authService.getToken();
client.setToken(newToken);

4. 数据类型不匹配

API 可能返回字符串格式的数字,请确保你的代码能够处理:

// ✅ 安全的类型转换
const balance = typeof account.balance === 'string'
  ? parseFloat(account.balance)
  : account.balance;

// ✅ 使用 SDK 提供的类型检查
const formatNumber = (value: string | number | null | undefined): number => {
  if (value === null || value === undefined) return 0;
  const num = typeof value === 'string' ? parseFloat(value) : value;
  return isNaN(num) ? 0 : num;
};

许可证

MIT