Skip to content

NDF Factory: サブスクリプションと Bedrock の token 課金 #2174

Closed as not planned

Description

@takemi-ohama

移した先: https://github.com/volareinc/nd-factory/issues/29(Factory の開発は移した先で続ける。手順は #2646)

親: #2168。前提: #2170。

依頼(原文)

Claude.aiと類似の認証・課金モデル(Free, 個人PRo, 個人Max, Team(standard/premium sear) etc)

(書き手: 利用者、日付: 2026-10-30(#2170 の依頼。認証とアカウントの確定仕様 の「依頼」が引く原文))

| 課金 | サブスクリプション。③ を選んだアカウントだけ、AI モデルの token 課金(Amazon Bedrock の課金をそのまま上乗せする)を足す |

(書き手: 起票者、日付: 2026-10-30。#2168 の「方針(利用者の決定)」の表の行で、利用者の決定を AI が書き留めたもの。最初の版(2026-10-30T01:11Z)は「サブスクリプション + AI モデルの token 課金(Amazon Bedrock の課金をそのまま上乗せする)」で、同じ日に「③ を選んだアカウントだけ」が足された。利用者の発言そのものは残っていない)

サブスクリプションと Amazon Bedrock の token 課金を扱う。token の課金は、実行の形態が ③ サービスの Bedrock(managed)のアカウントだけに掛かる(2026-10-30 の利用者の決定。確定仕様の「実行の形態」、PR #2190 )。① 手元のランナーと ② キーの持ち込みのアカウントはサブスクリプションだけを払う。

  1. managed のアカウントで使った Bedrock の token が利用者・チームごとに記録される(Neural Development Factory: NDF を Web UI で操作できる Software Factory のサービスにする #2168 の条件 8)
  2. サブスクリプションの料金に token の使用量を上乗せして請求できる
  3. 決済の手段(Stripe など)が設計で決まっている

(書き手: 起票者、日付: 2026-10-30。この課題の起票の本文。「前提にする契約」の節は #2170 の設計から写した契約で、下の「前提とする取り決め」に移した。この課題を進める指示は「人へ問わずに進め、決められない点は前提か未決として課題の本文へ書く」(書き手: AI の起動指示、日付: 2026-11-05)である)

解釈: Factory の料金プラン(個人 Pro・個人 Max・Team の席)を月額のサブスクリプションとして決済の提供元で支払えるようにし、料金プランの変更を支払いの確定から反映する。あわせて、実行の形態 managed で動いたスプリントの実行の使用量を、アカウントと利用者ごとの使用量の台帳に積み、UTC の暦月ごとに token の料金として月額に上乗せして請求する。local・byok の使用量は台帳に表示用として積むが、請求額には入れない。

起票者の推定

# 条件 推した出所 この要求での扱い
S1 token の課金は形態 managed の実行の分だけに掛かる #2168 の方針の表(2026-10-30 に足した行)・サービス全体の確定仕様 の「実行の形態」 受け入れ条件 3・4。確定仕様(PR 2190 で利用者が承認)に載った決定のため、利用者の決定として扱う
S2 決済の手段は Stripe などの提供元を使う この課題の受け入れ条件(案)3・#2168 の未決 受け入れ条件 13・未決 U6。提供元は設計で比べて決め、承認ゲート 1 で利用者が確かめる
S3 「Bedrock の課金をそのまま上乗せ」は、掛け率を掛けずに費用の額を請求する意味である #2168 の方針の表の「そのまま」 前提 3・未決 U2
S4 記録は「利用者・チームごと」に分ける この課題の受け入れ条件(案)1・#2168 の条件 8 受け入れ条件 2

目的

  • 有料の料金プランを利用者が自分で契約・変更・解約でき、サービスが支払いを受け取れるようにする
  • サービスの AWS アカウントの Bedrock で動かした分(形態 managed)の費用を、使ったアカウントへ請求できるようにする。local・byok のアカウントには token の費用を請求しない
  • アカウントの持ち主が、いま何にいくら掛かっているか(料金プランの月額・今月の使用量と token の料金)を画面と API で読めるようにする

前提

  • 前提 1: 請求の期間は UTC の暦月である。月の token の上限の判定(judgeLimits の tokensThisMonth)と同じ区切りにする
  • 前提 2: 使用量の行が managed の分かは、その行のスプリントの実行が開始で決めた実行の形態(実行の行の executionMode。実行の状態とデータの確定仕様 の I39)で決める。アカウントの形態を月の途中で変えても、すでに動いた実行の分の扱いは変わらない
  • 前提 3: token の料金は、managed の使用量の行の cost_usd(ランナーが claude の結果から写した額)の和に掛け率を掛けない額とする。使用量の行の入力の token は cache の読みと書きを合わせた数のため(実行の制御とランナーの確定仕様 の「ランナーの翻訳」)、token の数に単価を掛けると cache の割引と割増が消える。Bedrock の実際の請求との差は未決 U5 で扱う
  • 前提 4: 金額は通貨と組で持つ。最初の通貨は USD の 1 つで、端数の丸めは月とアカウントごとの合計で 1 回だけ行う。Bedrock の費用が USD で決まるため、為替の換算をこの課題で持たない。ほかの通貨は後から足せる形に留める(スプリント Value 5)
  • 前提 5: 実行の外の使用量(課題の候補の 1 回 1,500 token までの出力)と、取り込んだリポジトリのコードがタスクの役割で Bedrock を直に呼んだ分は、使用量の台帳に入らずサービスが持つ(実行の制御とランナーの確定仕様の「制約」の 2 行)
  • 前提 6: カードの番号など支払いの手段の値は、決済の提供元のページで利用者が入力し、サービスは受け取らない
  • 前提 7: Free の料金プランは決済を伴わない。新しいアカウントは今と同じく Free・local で始まる(NDF Factory: Cognito の認証(Google・GitHub)とプランごとの利用量 #2170 の I24)

対象範囲

含む:

  • 料金プランの月額の表(料金プラン・席の種類ごとの金額と通貨)を 1 か所に持つこと
  • 進捗の事象 usage.recorded から、アカウント・利用者・UTC の暦月ごとの使用量の台帳を作ること(3 つの形態とも。請求に積むのは managed だけ)
  • 決済の提供元での、個人の料金プラン(Pro・Max)とチームの席のサブスクリプションの契約・変更・解約と、支払いの確定から setPlan を呼んで料金プランを変える経路
  • 月の締めで、managed の token の料金を月額に上乗せして請求すること
  • 請求と使用量の画面(画面の地図の予定の行「請求と使用量」)と、同じ操作の API・MCP
  • 月の token の上限の判定へ渡す tokensThisMonth を、使用量の台帳から読むこと

含まない:

  • 料金プランの月額の値と token の料金の掛け率を決めること(未決 U1・U2。利用者が決める)
  • 為替の換算と USD 以外の通貨での請求
  • 税(消費税・適格請求書)の計算(未決 U4)
  • Bedrock の実際の請求(AWS の請求)との突き合わせ(未決 U5)
  • 決済の提供元のアカウントの開設・秘密の保存・Webhook の受け口の公開などインフラの定義(private リポジトリが作る)
  • 席を持つ利用者(チームの持ち主でない人)が請求を読むこと。画面の地図のとおり、読めるのは個人のアカウントの持ち主とチームの持ち主だけである
  • 使える量の表(LIMITS_TABLE)の値の見直し(台帳の実測が溜まった後に別に行う)

ドメインイベント

# イベント 引き金 失敗したとき 順序の前提
E1 使用量を記録した(usage.recorded を進捗の記録へ追記した) ランナーの送信 受け口が 503 を返し、ランナーが同じ冪等の鍵で送り直す 実行ジョブが始まっている
E2 使用量を台帳へ積んだ E1 積めなければ再試行する。同じ事象を 2 度積まない E1
E3 料金プランの変更を申し込んだ 持ち主が画面か API で選ぶ 決済の提供元へつながらなければ 503 を返し、料金プランを変えない アカウントが有る
E4 支払いを確定した 決済の提供元の通知 署名が合わない通知は拒み、何も変えない。同じ通知の再送は 1 度だけ反映する E3
E5 料金プランを変えた(plan.changed) E4・E8・E9 setPlan が版の競合で書けなければ(AccountUnavailableError)、提供元の再送で反映し直す E4・E8・E9 のどれか
E6 席を足した・種類を変えた・外した(seat.*) チームの持ち主の操作(#2170 の経路) 提供元の席の数を更新できなければ、未反映として残し再試行する チームのアカウントが有料の料金プランにある
E7 月を締めた UTC の月の初めの定期の処理 途中で失敗しても、同じ月の締めを打ち直して同じ額になる E2(前の月の使用量が積み終わっている)
E8 token の料金を請求へ足した E7 提供元へ足せなければ再試行する。同じ月のアカウントの額を 2 度足さない E7
E9 支払いが失敗した 決済の提供元の通知 未決 U3 の扱いに従う E8 か月額の請求
E10 解約した 持ち主の操作 提供元へつながらなければ 503 を返し、何も変えない 有料の料金プランにある
  • 引き金の無いイベントは無い。E5 の引き金は E4(契約・変更)・E9(未決 U3 で下げると決めた場合)・E10 の期間の終わり(解約)の 3 つである
  • 月の途中で free へ下げると、NDF Factory: Cognito の認証(Google・GitHub)とプランごとの利用量 #2170 の I21 で形態が local に変わる。それより前に動いた managed の実行の分は前提 2 により請求に残る

用語

用語 意味
使用量の台帳 進捗の事象 usage.recorded 1 件を 1 行として、アカウント・利用者・UTC の暦月・実行の形態ごとに積んだもの。請求に積むのは形態 managed の行だけ
token の料金 1 つのアカウントの 1 か月の、形態 managed の使用量の台帳の行の費用の和。料金プランの月額に上乗せして請求する
料金プランの月額 料金プラン(チームでは席の種類ごとの 1 席)の 1 か月の料金。金額と通貨の組で、1 か所の表が持つ
決済の提供元 サブスクリプションの契約と支払いの受け取りを担う外部のサービス(Stripe など)
月の締め UTC の暦月が終わった後に、アカウントごとの token の料金を確定して請求へ足す処理

受け入れ条件

使用量の台帳:

  • 1. 形態 managed のスプリントの実行が usage.recorded を送ると、そのアカウント・その実行を始めた利用者・その事象の時刻の UTC の暦月の行として、使用量の台帳に 1 行が増える。行はモデル・入力と出力の token・費用(金額と通貨)を持つ
  • 2. チームのアカウントでは、台帳の行を利用者ごとにも、チーム全体でも合計できる。持ち主が読むチームの使用量の合計は、利用者ごとの合計の和に等しい
  • 3. 形態 local・byok の実行の usage.recorded は台帳に積まれ画面に出るが、token の料金の額に入らない
  • 4. アカウントの形態を managed から local へ変えた後に締めても、変える前に始めた managed の実行の分は token の料金に残る(前提 2)
  • 5. 同じ冪等の鍵の usage.recorded が 2 度届いても、台帳の行は 1 行だけで、token の料金も 1 回分だけ増える
  • 6. 開始の判定(judgeLimits)へ渡す tokensThisMonth は台帳から読み、同じ月・同じアカウント(チームではその利用者)の managed の行の入力と出力の token の和に等しい

料金と請求:

  • 7. 料金プランの月額は、料金プランと席の種類ごとに金額と通貨の組で 1 か所の表にある。表に無い組で請求を作ろうとすると、請求を作らずに失敗として記録する
  • 8. 月の締めで、managed の行を持つアカウントごとに、token の料金(前提 3 の額。丸めは合計で 1 回)が決済の提供元の請求へ 1 件として足される。managed の行が 0 件のアカウントには足さない
  • 9. 同じ月の締めを 2 度打っても、1 つのアカウントに同じ月の token の料金は 1 件だけ足される
  • 10. Free の料金プランのアカウントには、月額も token の料金も請求されない

料金プランの変更と決済:

  • 11. 個人のアカウントの持ち主が画面から Pro か Max を選ぶと、決済の提供元の支払いのページへ移り、支払いが確定した通知を受けた後にだけ料金プランが変わる(setPlan を呼び、plan.changed の履歴の行が 1 つ増える)。支払いを確定せずに戻ったときは料金プランが変わらない
  • 12. チームの持ち主が席を足す・種類を変える・外すと、決済の提供元のサブスクリプションの席の数と種類がそれに合う
  • 13. 決済の提供元と、提供元との間の契約(サブスクリプションの形・通知の種類・日割りの扱い)が設計文書に決まっている
  • 14. 決済の提供元からの通知は、署名を確かめた後にだけ反映する。署名の無い・合わない通知は拒み、料金プランも台帳も変わらない。同じ通知の再送は 1 度だけ反映する
  • 15. 持ち主が解約すると、支払い済みの期間の終わりに料金プランが free に変わり、形態が managed なら同じ更新で local に変わる(NDF Factory: Cognito の認証(Google・GitHub)とプランごとの利用量 #2170 の I21)

画面・API・MCP:

  • 16. 請求と使用量の画面で、持ち主が今の料金プランと月額・今月の使用量(形態ごとの token と費用)・今月の token の料金の見込み・過去の月の請求の一覧を読める。画面の地図の「請求と使用量」の行の状態が「あり」になる
  • 17. 画面で読める請求と使用量は、同じ内容を API と MCP からも読める。経路は factory/contracts/openapi/ の OpenAPI に載り、入出力は配列で受け渡す
  • 18. 委任の資格情報では、料金プランの変更・解約・決済のページを開く操作を 403 human_only で断る。読み取りは委任の範囲に従って許す
  • 19. 持ち主でない者(席だけを持つ利用者を含む)が請求と使用量を読む・変える要求は、アカウントが無いときと同じ 403 forbidden と同じ本文で断る(NDF Factory: Cognito の認証(Google・GitHub)とプランごとの利用量 #2170 の I18)
  • 20. 請求と使用量の経路の呼び出しは、読み取りを含めて操作の記録に残る。記録に決済の提供元の秘密・支払いの手段の値・メールアドレスを書かない

退行しないこと:

  • 21. 料金プランの変更を決済の提供元の経路だけから行う。setPlan を利用者が直に呼べる経路を足さない
  • 22. 今のプラグインの利用者(CLI だけで使う人)の振る舞いと、plugins/・配布物が変わらない(境界の検査が通る)

非機能の条件

大項目 条件
可用性 決済の提供元が止まっていても、スプリントの開始・実行・使用量の記録は止まらない。止まるのは料金プランの変更と解約だけで、503 を返す
性能・拡張性 月の締めは、アカウントの数に比例して処理が増え、1 つのアカウントの失敗がほかのアカウントの締めを止めない
運用・保守性 料金プランの月額を変えるときは、月額の表の 1 か所と決済の提供元の価格の設定を直す。どちらを直すかを設計の運用の節に書く
セキュリティ 決済の提供元の秘密の鍵と通知の署名の鍵は、private リポジトリのインフラが置く秘密の ARN からだけ読む。リポジトリと記録に値を書かない(C1)。カードの番号を受け取らない(前提 6)

影響

対象 影響
公開インタフェース 変わる。請求と使用量の経路と、決済の提供元の通知の受け口を足す。既存の経路の形は変えない
データ 使用量の台帳・アカウントと決済の提供元の顧客の対応・月の締めの記録を足す。既存の表のスキーマは変えない。表の定義は private リポジトリへ渡す
既存の振る舞い 開始の判定の tokensThisMonth の読み元が、進捗の記録の直の和から使用量の台帳へ変わる(値は受け入れ条件 6 で同じになる)

検証手段

項目 手段
テスト・静的解析・型検査 factory/ で npm run check(CI の factory-check)。決済の提供元はテストの中で偽物に置き換える
境界 python3 scripts/check-service-boundary.py
手動確認 決済の提供元のテストの環境で、Pro を契約 → 確定の通知 → 料金プランが変わる → managed で 1 本流す → 月の締めを手で起こす → 請求に token の料金が 1 件足される、を人が見る(リリース後テスト)

前提とする取り決め

#2170 の設計で決まった契約で、変えるときは確定仕様を直す Pull Request を先に出す。

項目 参照先 / 決めたこと
アカウントの文脈 resolveAccount(caller, accountId?) から AccountContext を得る。他人のアカウントは同じ 403 で返す(認証とアカウントの確定仕様 の「他の子課題へ公開する関数」・I18)
料金プランの変更 setPlan(accountId, plan) を呼ぶ。PlanError・AccountUnavailableError の扱いと、free へ下げるときの形態の変更は同 I13・I21
使える量 値は factory/api/src/accounts/limits.ts の LIMITS_TABLE。月の token は入力と出力の和で cache を含む(同「使える量の表と判定」)
請求の期間の判定 アカウントの表の履歴の行(plan.changed・seat.*・execution_mode.changed)を使う(同 I20)
使用量の事象 usage.recorded は 3 つの形態とも届き、請求へ積むのは managed だけ(サービス全体の確定仕様 の「実行の形態」・決定 14)
プロジェクト構造 コードは factory/api/・factory/web/ の npm の workspaces に足す。経路は factory/contracts/openapi/ の OpenAPI が正。インフラの定義と秘密はこのリポジトリに置かない(factory/README.md の「パッケージの管理」・AGENTS.md)
コーディング規約 エラーの本文は {"error": {"code", "message"}}。画面の文言は翻訳のファイル、金額は Money({ amount, currency })で整形する(画面の部品の確定仕様 の「文言と書式」)
テスト戦略 台帳・締め・通知の反映はメモリの保存と偽の決済の提供元で単体のテスト、経路は API のテスト、画面は既存の画面のテストの形に従う

境界

区分 内容
常に行う npm run check と境界の検査を通す。新しい経路を OpenAPI と操作の記録に載せる
確認してから行う 決済の提供元の選定と依存パッケージの追加・新しい表の追加(承認ゲート 1 で人が見る)。料金プランの月額の値(未決 U1)
行わない 決済の提供元の本番の鍵を扱うこと・インフラの定義をこのリポジトリに書くこと・LIMITS_TABLE の値を変えること

未決

# 項目 誰が決めるか 期限
U1 料金プランの月額(個人 Pro・個人 Max・Team standard の 1 席・Team premium の 1 席)の値 利用者 本番への配布の承認(承認ゲート 2)まで。実装は表の値を仮の値で作る
U2 token の料金に掛け率を掛けるか。推奨は掛けない(原文の「そのまま上乗せ」。前提 3) 利用者 承認ゲート 1
U3 支払いが失敗し、決済の提供元の再試行が尽きたときの扱い。推奨は、尽きた時点で free へ下げる(形態が managed なら local へ。I21) 利用者 承認ゲート 1
U4 税(消費税・適格請求書)の扱い。推奨は、決済の提供元の税の機能に任せ、この課題では計算しない 利用者 本番への配布の承認まで
U5 前提 3 の額と Bedrock の実際の請求との突き合わせを、いつ・どこで行うか(AWS の請求の読み出しは private リポジトリのインフラの権限が要る) 利用者 本番への配布の承認まで
U6 決済の提供元(Stripe などから設計で比べて推す) 設計で推し、承認ゲート 1 で利用者が確かめる 承認ゲート 1

🤖 Generated with Claude Code

進行

モード: —

  • 要求と受け入れ条件 — 2026-11-05 21:33
  • 作業場所の用意
  • 設計 — 2026-11-05 21:37
  • 素材の収集と出典の確定
  • ドキュメント再構成
  • ドキュメントレビュー
  • 計画
  • 実装
  • 構造改善
  • 実装レビュー
  • 完了判定
  • Pull Request
  • 確定仕様化
  • 後片付け
  • 配布
  • 体裁レビュー
  • リリース後テスト
  • 振り返り
  • 棚卸し

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions