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 })
}

コメント