mcpField
M
metered-usage-best-practices

metered-usage-best-practices

github.com/getpaykit开发1144 次安装30 周前更新
代码仓库

关于 metered-usage-best-practices

这份 Paykit 使用指南介绍如何在应用中检查功能权限并记录用量,适用于实现按用量限制的产品功能。它涵盖预检、扣减、余额读取及权益重置行为。

主要功能

  1. 01检查布尔型与计量型权益
  2. 02预检所需用量余额
  3. 03报告消耗并扣减用量
  4. 04读取余额、限额和重置时间
  5. 05按周期延迟重置权益

适用场景

  1. 01执行操作前验证用户额度
  2. 02为批量任务预留所需单位
  3. 03用报告操作完成原子扣减
  4. 04向用户展示剩余量和重置时间

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/getpaykit/skills --skill metered-usage-best-practices

安装后在 Claude Code 中输入/metered-usage-best-practices确认已加载。

详细介绍

计量用量

使用 check() 和 report() 控制访问权限并跟踪用量。

check()

检查客户是否可以使用某项功能。

const result = await paykit.check({
  customerId: "user_123",
  featureId: "messages",
})

if (!result.allowed) {
  throw new Error("Usage limit reached")
}
参数
参数必填说明
customerId是你应用的用户 ID
featureId是要检查的功能(类型安全)
required否检查是否至少还剩这么多单位
返回值
interface CheckResult {
  allowed: boolean
  balance: {
    limit: number
    remaining: number
    resetAt: Date | null
    unlimited: boolean
  } | null
}

对于布尔型功能: allowed 为 true/false,balance 为 null。

对于计量型功能: 如果 remaining > 0(或 remaining >= required),则 allowed 为 true;balance 包含用量详情。

预先检查可用量

在批量操作前,使用 required 检查是否有足够的剩余单位:

const { allowed } = await paykit.check({
  customerId: "user_123",
  featureId: "api_calls",
  required: 50,
})

if (!allowed) {
  throw new Error("Not enough API calls remaining")
}

report()

使用后扣减用量。

const result = await paykit.report({
  customerId: "user_123",
  featureId: "messages",
  amount: 1,
})

if (!result.success) {
  // Usage limit exceeded
}
参数
参数必填说明
customerId是你应用的用户 ID
featureId是要扣减的功能(类型安全)
amount否消耗的单位数。默认值:1
返回值
interface ReportResult {
  success: boolean
  balance: {
    limit: number
    remaining: number
    resetAt: Date | null
    unlimited: boolean
  } | null
}

如果客户的剩余余额不足,success 为 false。


用法模式

执行操作前进行权限检查(先检查,再操作)
const { allowed } = await paykit.check({
  customerId: userId,
  featureId: "messages",
})

if (!allowed) {
  return { error: "Message limit reached. Upgrade your plan." }
}

await sendMessage(content)

await paykit.report({
  customerId: userId,
  featureId: "messages",
})
原子式检查并扣减

对于更简单的流程,可以跳过 check(),直接使用 report()。如果余额不足,操作会失败:

const { success, balance } = await paykit.report({
  customerId: userId,
  featureId: "api_calls",
})

if (!success) {
  return Response.json(
    { error: "API call limit exceeded", resetAt: balance?.resetAt },
    { status: 429 },
  )
}

// Process the API call
布尔型功能权限检查
const { allowed } = await paykit.check({
  customerId: userId,
  featureId: "custom_branding",
})

if (!allowed) {
  return { error: "Custom branding requires a Pro plan" }
}
向用户显示用量
const { balance } = await paykit.check({
  customerId: userId,
  featureId: "messages",
})

// balance.remaining  // units left
// balance.limit      // total allowed
// balance.resetAt    // when usage resets
// balance.unlimited  // true if no limit

权益重置

计量型权益采用惰性重置,不会通过 cron 执行。重置时间过后,下一次调用 check() 或 report() 时才会触发重置。

重置间隔行为
"day"从首次使用起每隔 24 小时重置
"week"每隔 7 天重置
"month"每个日历月重置
"year"每个日历年重置

直接读取权益

也可以从客户对象中读取权益:

const customer = await paykit.getCustomer({ id: "user_123" })

for (const [featureId, entitlement] of Object.entries(customer.entitlements)) {
  console.log(featureId)            // "messages"
  console.log(entitlement.balance)  // current balance
  console.log(entitlement.limit)    // max allowed
  console.log(entitlement.usage)    // consumed
  console.log(entitlement.unlimited) // boolean
  console.log(entitlement.nextResetAt)
}

完整示例:带用量限制的 AI 聊天

// lib/paykit.ts
const messages = feature({ id: "messages", type: "metered" })
const proModels = feature({ id: "pro_models", type: "boolean" })

const free = plan({
  id: "free",
  group: "base",
  default: true,
  includes: [messages({ limit: 50, reset: "day" })],
})

const pro = plan({
  id: "pro",
  group: "base",
  price: { amount: 20, interval: "month" },
  includes: [messages({ limit: 2_000, reset: "day" }), proModels()],
})

// app/api/chat/route.ts
export async function POST(request: Request) {
  const { userId, model, content } = await request.json()

  // Check message quota
  const { allowed, balance } = await paykit.check({
    customerId: userId,
    featureId: "messages",
  })

  if (!allowed) {
    return Response.json({
      error: "Daily message limit reached",
      resetAt: balance?.resetAt,
    }, { status: 429 })
  }

  // Check model access
  if (model === "gpt-4") {
    const { allowed } = await paykit.check({
      customerId: userId,
      featureId: "pro_models",
    })
    if (!allowed) {
      return Response.json(
        { error: "Pro models require a Pro plan" },
        { status: 403 },
      )
    }
  }

  const response = await generateResponse(model, content)

  // Decrement usage
  await paykit.report({
    customerId: userId,
    featureId: "messages",
  })

  return Response.json({ response })
}

评论

支持 Markdown