metered-usage-best-practices について
Paykitの利用ガイドで、アプリの機能アクセス確認と消費量の記録方法を説明します。利用制限の実装に向け、事前確認、減算、残量の取得、権利のリセットを扱います。
主な機能
- 01ブール型と従量型の権利確認
- 02必要な利用残量の事前確認
- 03消費量の報告と利用量の減算
- 04残量、上限、リセット時刻の取得
- 05期間ごとの遅延リセット
活用シーン
- 01操作前のユーザー利用枠の確認
- 02一括処理に必要な単位数の確認
- 03報告による原子的な減算
- 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 対応