本文へ

ガイド

レシピ:API クレジット

API や AI 機能の前払いクレジットを販売し、リクエストごとに 1 つ差し引きます。残高が足りなければ、チャージのリンク付きで 402 を返します。

Markdown で開く

購入者はクレジットパックを前払いし、リクエストごとにいくつか消費します。記録は Perly Pay が行います:消費はマイナスにならないアトミックな 1 回の呼び出しで、購入者ごとに専用のチャージページがあります。自動で請求されることはなく、クレジットがなくなれば購入者がチャージします。クレジットのすべては Sell credits に。ここではそれを使ったレシピを紹介します。

アシスタントと一緒に:このレシピは、Perly Pay プラグインの sell-credits スキルがあなたのプロジェクトで実行します。動くサンプルは、プラグインのリポジトリの examples/api-credits(MIT)です。

仕組み

  1. 1

    クレジットパック

    model: "credit_pack"、セント単位の unit_amount、credits を持つ商品と価格。モードごとに 1 回。

  2. 2

    ユーザーが顧客に

    external_id(あなたのユーザー ID)で一度作成。以降、credits.* はその ID を受け付けます。

  3. 3

    チャージ

    credits.topUpUrl(userId) は購入者専用のページ:パックを選んで支払います。支払い済みのパックごとにクレジットが加算されます。

  4. 4

    リクエストごとに消費

    まず credits.consume(userId, cost, { idempotencyKey })、それから処理。同じキーでの再試行は最初の消費を返すため、キーはリクエストごとにサーバーが作り、呼び出し元からは決して受け取りません。

  5. 5

    足りない?

    insufficient_credits(409)は何も差し引きません:500 ではなく、チャージページ付きで 402 を返してください。

Spend one request's credits
try {
  await pay.credits.consume(userId, cost, { product: PRODUCT, action: "api", idempotencyKey: `req-${userId}-${requestId}` });
} catch (e) {
  if (e.code !== "insufficient_credits") throw e;
  return reply(402, { top_up: await pay.credits.topUpUrl(userId, { product: PRODUCT }) });
}
// now do the work

サンプルを動かす

  1. 1

    インストール

    examples/api-credits で npm install(Node 20.12+)。

  2. 2

    キー

    .env.example を .env にコピーし、PERLY_PAY_SECRET_KEY にテストキー(sk_test_…)を書きます。

  3. 3

    準備

    npm run setup があなたのキーで商品と価格を作成し、.env 用にその ID を表示します。

  4. 4

    起動

    README のとおり npm start(または npm run dev)。

  5. 5

    ウォレットなしで支払う

    POST /v1/shout を呼び(402)、POST /top-up が返すページを開き、パックを選び、請求書で Simulate payment を押してもう一度呼びます:200 と X-Credits-Remaining: 499。

落とし穴

  • 冪等キーなしの消費:再試行されたリクエストで二重に差し引かれます。
  • クライアントが送るもの(Idempotency-Key ヘッダー)を消費のキーにすること:同じキーは最初の消費を成功として返し、クライアントは支払わずに再び処理を受けられます。
  • 残高を確認してから消費する 2 段階にすること。consume 自体がマイナスを防ぎます。残高は表示用です。
  • 処理してから消費すること。逆です:まず消費。
  • 本番キーでテストの ID を使うこと。商品、価格、Webhook はひとつのモードに属します:公開前に sk_live_… で設定をやり直してください。