feat(api): Stripe(価格・商品・請求・Checkout)を oRPC 契約ファーストで公開する - #38
Conversation
Stripe をラップした prices エンドポイントを契約ファーストで追加し、以後の Stripe 由来 API の雛形とする2パターンを確立する。 - retrieve: 単一の Price を返す。 - list: エンベロープ(has_more/data)は自前 zod で構造検証し、各要素は Stripe ベースの派生型 PriceView。product_id で Stripe の product フィルタに写す。 出力は両者とも PriceView。Stripe の metadata は Checkin が使う traq_id だけに 絞り、内部キーを晒さない。この絞り込みは型では強制できない(Stripe.Metadata の インデックスシグネチャが構造的に代入可能なため)ので、変換を toPriceView() の 1箇所に閉じ込める。top-level の Stripe 標準フィールドはそのまま公開するため、 必要な項目が増えても契約を変えずに済む。Stripe SDK の lastResponse は非列挙 プロパティのためシリアライズされず漏れない。 Stripe クライアントは db と同様にリクエストコンテキスト経由で遅延注入する。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
consistent-type-assertions を assertionStyle:'never' で有効化し、型チェッカーを 迂回して実際とズレた型を黙って通す `as` を禁止する(`as const` と名前空間 import `* as` は対象外)。テストのスタブなど安全が確認できる箇所は、なぜ安全かを添えた eslint-disable で個別に許可する。既存スタブは Context / Stripe.Price への最小 キャストに集約した。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
一覧に limit / starting_after を選択的透過で追加し、フロントが Stripe の カーソルページングを使えるようにする。支援する入力だけを受け、expand 等の その他 Stripe パラメータは受け付けない(expand はネストした product.metadata 等の漏洩経路になるため)。zod の入力スキーマが未知キーを除去するので、この 制限は検証層で強制される。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- コメントの「支援するパラメータ」を「対応するパラメータ」に修正(不自然な直訳) - eslint コメントから紛らわしい `import * as` の言及を削除し、`as const` のみが 本ルールの例外である旨に是正 - list ハンドラのコメントに、フロントが data 末尾の Price ID を次回の starting_after に使う導出手順を明記 - product_id とページネーションを併用する経路の転送テストを追加 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stripe をラップする管理系エンドポイント群を契約ファーストで実装する。 セッション不要で Stripe SDK だけで完結する範囲(カタログ/請求の管理面)を対象とし、 会員フロー(認証・セッション・webhook 依存)は #16 #17 #18 に切り出した。 追加した契約とバックエンド: - products: list / update - invoices: list / create(Invoice 作成 → 価格を項目化 → 確定して支払い URL を返す) - checkout: listSessions - prices: update を追加、list に active/type/ending_before フィルタを拡充 共通化: - 出力の metadata 絞り込みを汎用の narrowMetadata<T> と型 WithTraqId<T> に一般化し、 全リソースで変換点を1箇所に集約。 - ページネーション・書き込み可能 metadata の入力を params.ts に共通化。 - 入力は選択的透過(フィルタ + ページネーション、expand 不可、mutation の metadata は traq_id のみ)。 テスト: 各リソースの Stripe パラメータ写像・metadata 絞り込み、invoices.create の 多段フローと支払い URL 欠如時のエラーを検証(テスト共有ヘルパ test-utils を追加)。 認可は未実装のまま(#15)。本 PR の管理系エンドポイントは認可導入時に保護対象へ含める。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
セキュリティ: - invoices.create を collection_method: send_invoice に固定(days_until_due:30)。 既定の charge_automatically は finalize 時に顧客の既定支払い方法へ自動課金され得るが、 このエンドポイントは支払い URL を返すリンク払いを意図しているため。 型・テスト: - stripeFixture を overrides: Partial<T> にし、呼び出し側でトップレベルのフィールド名 タイポをコンパイル時に検出できるようにした(no-unnecessary-type-parameters の disable も不要化)。 - テスト追加: invoices.create の metadata 未指定ブランチ、invoices.list の subscription_id 写像、prices/products の update の refine(最低1フィールド)負テスト。 コメント: - views.ts の設計根拠を narrowMetadata 側に集約し、ネストした metadata (Invoice.lines[].metadata 等)が expand せずとも既定レスポンスに含まれ透過する点を 正確化。metadata 以外の PII の公開範囲と併せて #15 で扱う旨を明記。 - 「選択的透過」の定義と expand 除外の原則を params.ts に集約。 - 「支払い URL」の用語を統一。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
機微リソース(Invoice / CheckoutSession)の出力を、Stripe オブジェクトの透過から 公開フィールドを明示列挙する allowlist へ切り替える。透過 + metadata 絞り込みは、 metadata 以外の PII(customer_email / customer_name / 住所 等)やネストした metadata (lines[].metadata 等)を既定で素通ししていた。allowlist は既定で安全(列挙外は出ない)で、 将来 Stripe が追加するフィールドも自動では露出しない。 - 契約: InvoiceView / CheckoutSessionView を本物の z.object で定義。customer / payment_intent は ID のみ、metadata は traq_id のみ。status は forward-compat のため z.string()(将来の status で出力検証が落ちないように)。 - 実装: ハンドラで各フィールドを明示的に組み立てる(oRPC のストリップ挙動に依存せず、 列挙したフィールドしか出さないことを保証)。ID 抽出 idOf / traq_id 抽出 traqIdOf を追加。 - カタログ(Price / Product)は PII を持たないため透過(narrowMetadata)を維持する。 - テスト: fixture に customer_email 等の PII と内部 metadata を入れ、出力に含まれない ことを toStrictEqual で厳密に検証。 出力 PII/ネスト metadata の露出面は #15 で挙げていたが、機微リソースについては本 PR で allowlist により解消した。認可(#15)は引き続き必要。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- 全 mutation(prices/products の update、invoices の create)の入力から metadata を 除去。traq_id はレスポンス専用とし、クライアントによる書き込み(=本人紐付けの偽造)を 排除する。顧客との紐付けはセッション実装時にサーバ側で行う(#18)。カタログ metadata の 公開方針は別途検証する。writableMetadata は削除。 - invoices.create: 支払い期限 days_until_due をハードコード(30)から任意入力へ変更 (期限は運用で決めるため)。リトライ安全のための idempotency_key を任意入力で追加し、 指定時のみ Stripe のリクエストオプションに渡す。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- checkout.listSessions → checkout.sessions.list(Stripe SDK の stripe.checkout.sessions.list の構造に合わせる)。 - 一覧フィルタ・作成入力の param 名を Stripe の bare 名に統一する (product_id→product, customer_id→customer, subscription_id→subscription, payment_intent_id→payment_intent, price_id→price)。サンプル定義は prices が product(bare)・invoices/checkout が customer_id(suffix)で 不統一だったのを Stripe-exact に揃えた。ハンドラの写像も同名になり単純化。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
traq_id は自前 DB を単一ソースとして持ち、Stripe metadata には持たせない方針に 確定したため、API 出力から traq_id を撤去する。出力で traq_id が必要になれば customer→DB 逆引きで解決する(#18)。 - カタログ(Price/Product): 透過するが metadata は出力しない(omitMetadata で除く)。 PriceView/ProductView は Omit<Stripe.X, 'metadata'>。 - 機微(Invoice/CheckoutSession): allowlist から metadata フィールドを外す。 - narrowMetadata / WithTraqId / traqIdOf を撤去(idOf は維持)。 packages/api-contract/src/views.ts を削除。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
レビュー(6観点)を反映。カタログ(Price/Product)も Invoice/CheckoutSession と同様に
公開フィールドを明示列挙する自作 z.object へ統一し、Stripe SDK の型を公開契約から外す。
これにより (a) 出力の実行時強制(z.custom の素通しを排除)、(b) ネスト metadata の
漏洩ベクタ閉塞、(c) api-contract の stripe 依存の削除、をまとめて達成する。決済プロバイダ
の実装は toXView の背後(薄いアダプタ)に退く。
- Price/Product: 自作 PriceView/ProductView(z.object) + toPriceView/toProductView。
z.custom / omitMetadata を廃止。api-contract から stripe 依存を削除。
- 一覧レスポンスの {has_more, data} を listEnvelope() に共通化。
- invoices.create の idempotency_key を必須化(多段フロー全体をリトライ安全に)。
- prices.update は active を必須化(単一フィールドなので refine 不要)。
- コメント修正: send_invoice は finalize だけでは顧客にメールを送らない旨に訂正
(メール送信フローは別 issue)。「写像」等の不自然な表現・冗長を整理。
- stripe util に未設定時の fail-fast を追加。
- テスト刷新: 全 view の allowlist を toStrictEqual で厳密検証、idOf の展開オブジェクト/
null 分岐、param 分岐(collection_method 等)を追加。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- invoices.create の idempotency_key を max(246) に(実装が末尾に ":finalize" 等の suffix を連結して Stripe に渡すため、上限 255 を超えないようにする)。 - Stripe クライアントの apiVersion を SDK が生成された版に明示ピン(省略時にアカウントの 既定 API バージョンへ従い、SDK の型と実行時レスポンス形状がズレるのを防ぐ)。 - view の doc コメントを正直化: 「決済プロバイダから切り離す」は実態と乖離していたため、 実際の目的(PII・内部/将来フィールドを漏らさない allowlist と、クライアント向けの安定契約) に修正。フィールドの形状・語彙は意図的に Stripe に合わせている旨を明記。 - テスト補強: idOf の展開オブジェクト分岐(prices.product / products.default_price / checkout の customer・payment_intent)を各リソースで固定、prices.update の active 必須の負テストを追加。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
一覧レスポンスを { has_more, data } から { data, next_cursor } に変更する。
カーソル(次回の starting_after に渡す値)の導出はサーバーが担い、続きがあれば
Stripe ソース末尾要素の id、無ければ null を返す。クライアントは next_cursor を
そのまま渡すだけでよく、将来カーソル戦略を変えてもこのレスポンス契約を保てる。
逆方向カーソル ending_before は公開契約から外し、前方ページングのみを提供する。
next_cursor 単体では逆方向の継続位置(先頭要素を ending_before に渡す)を表現できず、
受け付けたまま前方カーソルしか返さないと逆方向ページングが壊れるため。「前に戻る」は
クライアントが starting_after の履歴を保持して実現する。
toListResponse ヘルパで4リソース(prices/products/invoices/checkout)のレスポンス
構築を共通化し、カーソルは View 非依存の Stripe ソース(page.data)の id から導出する。
境界(末尾要素選択・空 data → null・View 非依存のカーソル導出)は views.test.ts で
直接検証する。
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
api-contract は Stripe 型を使わない自作 zod ビューに移行済みで、package.json の 依存からは stripe を既に除いていたが、pnpm-lock.yaml が未更新のままだった。lock を package.json に追従させる。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
移行容易化を主目的にしない旨と、その根拠(入力語彙・カーソル・ハンドラの 書き換えが要る)は PriceView ではなく契約全体に関わる設計判断で、1 つの View のドキュメントに置くのは非対称でノイズになるため削る。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PriceView コメントから外した設計判断(Stripe 語彙に合わせるが移行容易化は 目的でない旨)を、恒久的な設計判断の置き場である project.md の規約に移す。 allowlist 出力ポリシー自体も従来 View コメントにしか無かったため併せて明文化し、 View 単体でなく契約全体の方針として参照できるようにする。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
「同定データは DB を単一ソースとし Stripe metadata には持たせない」という API 全体の判断が InvoiceView と CheckoutSessionView のコメントに重複していた。 根拠を project.md の規約に集約し、各 View コメントは「traq_id は出力に含めない」 という View ローカルな事実と参照先だけに絞る。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
collection_method を send_invoice に固定し明示的に確定(finalizeInvoice)する一方で days_until_due を optional のまま指定時のみ渡していた。Stripe は send_invoice の確定時に 支払い期限を要求するため、未指定の呼び出しは実行時に必ず 400 になり、汎用 500 として 返っていた。契約で必須化し常に渡すことでこの経路を塞ぐ。 テストも更新: 未指定は入力バリデーションで reject、0(即時期限)は 0 のまま透過、 確定 URL が null/undefined の両腕をエラー化することを検証する。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Price.product は Stripe 上で必ず存在する(string | Product | DeletedProduct、削除済みでも id は残る)が、View が nullable でクライアントに発生し得ない null 分岐を強制していた。 非 null 参照専用の requireIdOf を追加し、priceView.product を z.string() に絞る。 あわせて idOf を null だけでなく undefined も受けるよう広げ、optional な参照 (Product.default_price)呼び出しの `?? null` を不要にして境界を idOf 内に閉じる。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
全プロシージャが無認証の現状で、invoices.create(実際に課金 Invoice を確定し支払い URL を発行)・prices.update・products.update が同じ無認証ルートに載っており、コード上の 歯止めが無く「main が未到達」だけが安全担保になっていた。 Context に mutationsEnabled を追加し、変更系ハンドラの先頭で assertMutationsEnabled を 呼んで false の環境では FORBIDDEN を返す。Nitro 側は runtimeConfig の enableUnsafeMutations (既定 false、NUXT_ENABLE_UNSAFE_MUTATIONS=true で有効化)から注入する。認可(#15)導入時に 本来の認可チェックへ置き換える暫定措置。 各変更系に mutationsEnabled:false で拒否されるテストを追加。testContext は既定 true。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
コメント層の why が、契約全体の方針を各 View・各所に重複させたり、書くべきでない対象に 結合させていた。配置を整理する。 - 出力の文字列フィールドを enum で狭めない方針(3 View に重複)と、変更系を既定で無効化し #15 で認可に置き換える方針(6 箇所以上に散在)を project.md の規約に集約し、各所は 固有情報と短い参照だけに絞る。allowlist・移行方針・traq_id と扱いを揃える。 - 契約 invoices.ts の冪等キーコメントが実装の suffix 文字列(":finalize" 等)に結合して 上限値の根拠を説明していたのを、契約が守る不変条件(suffix 予約分を上限内に収める)の 記述に変える。#15 の名前空間化注記は実装側の1箇所に寄せる。 - orpc.ts の Context フィールドコメントを参照に短縮し機構説明を assertMutationsEnabled に、 invoices/prices ハンドラの重複・再説明コメントを削除して4ハンドラの体裁を揃える。 - 日本語の不自然さを修正(明示的に固定する / 対応づけ / falsy / 両ケースを個別に検証)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
repo のどこにも記録が無かった2つの API 規約を正典に残す。 - フィールド名の snake_case 統一(DB 層の camelCase とは別規約で、Stripe 由来フィールドが snake_case で不可避なため一貫性を優先する判断)。従来はコード上の慣習でしか伝わらなかった。 - 入力の選択的透過と expand 非受理。出力 allowlist は既に project.md にあるのに入力側の方針が params.ts のコメントにしか無く非対称だったため、方針を project.md へ移し params.ts は参照に絞る。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
出力を allowlist にして全フィールドを手で列挙するようになった結果、当初の根拠だった 「透過ゆえに snake_case が避けられない」は成立しなくなった(has_more は next_cursor に 置き換わり出力にも出ない)。現在の実質的な根拠である「入力を Stripe SDK へ 1:1 で渡せ、 出力 View も Stripe と 1:1 で変換層が薄い」に書き換える。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
出力を allowlist(全フィールド手書き)にしたことで、当初 snake_case を選んだ根拠 (Stripe 透過ゆえに不可避)が消えた。消費側(TypeScript/Vue)と DB 層(drizzle)の 慣習に揃え、契約の入出力フィールドを camelCase に統一する。 - 出力 View(unitAmount, amountDue, defaultPrice, paymentIntent 等)と一覧 envelope (nextCursor)、create の出力(invoiceId, paymentUrl)を camelCase 化。 - 入力(startingAfter, collectionMethod, daysUntilDue, idempotencyKey, paymentIntent)を camelCase で受け、ハンドラで Stripe の snake_case パラメータへ変換して渡す。 - Stripe の enum の値(one_time, send_invoice 等)はデータ値なので変換しない。 - project.md の命名規約を camelCase に更新。消費側 UI は未接続(health.check のみ)のため クライアント移行は不要。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
camelCase 移行で追加した入力変換コメントに、既に是正済みの不自然な語「写像」を 再び使っていたため「変換」に統一する。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
規約や契約コメントの理由が「Xにする。Yのため。」の Y が言い換えや浅いジェスチャーに とどまり、なぜその判断が正しいかを説明できていなかった。各理由を、避けられる具体的な 失敗か退けた代替案とその不利を名指しする形に書き直す。 - project.md: camelCase(退けた snake_case の利点と、allowlist ゆえ自動では得られない点)、 allowlist 出力(透過が招く PII/内部/将来フィールドの無審査流入)、選択的入力(expand が allowlist を迂回する漏洩経路)、enum 非縮小(z.enum 固定が新値で 500 を招く)、変更系ガード (「到達不能」だけが歯止めになる危険)を実質的な理由に。 - traq_id: 「metadata に持たせない」と「出力に載せない」を分けて各々正当化(後者は内部同定子の 露出による名寄せ・相関面の拡大)。 - params.ts: 前方カーソルのみの理由を循環論法から、単一 nextCursor がカーソル導出戦略の差し替え 自由度を保つこと・ending_before 公開の不利へ。履歴なしクライアントは戻れない制約と双方向への 拡張パスも明記。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
後で変える時のコストと、今その決定に足る情報があるかの2軸で、暫定にするか今決めるかを 判断する方針を明文化する。暫定は設計で可逆に保ち、見直す条件とともに印を残す。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Stripe 系のプロシージャが揃い、health.check の疎通確認としての役目は終えた。契約・ルーター・ ホームページの呼び出し・専用テストを削除し、ホームページは静的にする。 db は現状どのプロシージャも参照しないが、将来のドメイン機能のため Context に残す。Nitro の 遅延ゲッターと README の説明を、health 前提から現状(db 未使用・stripe は全プロシージャで使用)に 合わせて更新する。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
prices は retrieve/list/update を持つのに products は list/update のみで、単一 Product を ID で取得する手段が契約に無かった。消費側がまだ無く追加コストが小さい今、対称性を揃える (#25)。allowlist・展開参照の ID 正規化の検証テストも追加。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
このプロシージャは下書きを作るのではなく、価格を項目化し確定(finalize)して支払い可能な Invoice を発行する金銭的な確定操作。create という名前はその効果を過小に表すため、確定・発行を 表す issue に改める(#26)。消費側がまだ無く改名コストが小さい今に行う。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
シークレット未設定を最初の Stripe 利用時ではなくデプロイ時に検知するため、Nitro の起動 プラグインで存在を検証し、欠落なら起動を失敗させる(#23)。検証は本番のみで、ローカル開発は 鍵なしでも起動できる(Stripe を使うプロシージャは呼ばれた時点で useStripe が明示エラーにする)。 server/plugins は起動時自動登録で静的 import 経路が無く、typescript-eslint の projectService が 所有 tsconfig を見つけられないため、このディレクトリだけ Nitro のサーバー tsconfig を明示する ESLint 設定を追加する。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/nuxt.config.ts`:
- Around line 8-9: Update the runtime configuration flow around stripeSecretKey
so the server-side shared resolver reads the secret at application runtime and
supplies it to runtimeConfig.stripeSecretKey instead of evaluating
process.env['STRIPE_SECRET_KEY'] during nuxt.config.ts build-time loading.
Preserve NUXT_STRIPE_SECRET_KEY support and document a single, consistent
precedence between the two environment variable forms in the README.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 6d7a691f-1f24-4651-9e20-51067a7106bf
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (32)
README.mdapps/web/app/pages/index.vueapps/web/nuxt.config.tsapps/web/package.jsonapps/web/server/plugins/validate-config.tsapps/web/server/routes/rpc/[...].tsapps/web/server/utils/stripe.tseslint.config.mjsopenspec/project.mdpackages/api-contract/src/checkout.tspackages/api-contract/src/health.tspackages/api-contract/src/index.tspackages/api-contract/src/invoices.tspackages/api-contract/src/params.tspackages/api-contract/src/prices.tspackages/api-contract/src/products.tspackages/api-contract/src/router.tspackages/api/package.jsonpackages/api/src/checkout.test.tspackages/api/src/checkout.tspackages/api/src/invoices.test.tspackages/api/src/invoices.tspackages/api/src/orpc.tspackages/api/src/prices.test.tspackages/api/src/prices.tspackages/api/src/products.test.tspackages/api/src/products.tspackages/api/src/router.test.tspackages/api/src/router.tspackages/api/src/test-utils.tspackages/api/src/views.test.tspackages/api/src/views.ts
💤 Files with no reviewable changes (3)
- apps/web/app/pages/index.vue
- packages/api-contract/src/health.ts
- packages/api/src/router.test.ts
| // 実行時に NUXT_STRIPE_SECRET_KEY で上書き可能。サーバー専用(先頭に public を付けない)。 | ||
| stripeSecretKey: process.env['STRIPE_SECRET_KEY'] ?? '', |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,220p' apps/web/server/plugins/validate-config.ts
sed -n '1,260p' apps/web/server/utils/stripe.ts
rg -n -C 3 'stripeSecretKey|STRIPE_SECRET_KEY|NUXT_STRIPE_SECRET_KEY' apps/webRepository: traPtitech/Checkin
Length of output: 3331
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import re
config = Path('apps/web/nuxt.config.ts').read_text()
validate = Path('apps/web/server/plugins/validate-config.ts').read_text()
stripe = Path('apps/web/server/utils/stripe.ts').read_text()
print('nuxt runtimeConfig line 9:')
m = re.search(r"stripeSecretKey:\s*([^,\n]+)", config)
print(m.group(0).strip() if m else '<not found>')
def resolve_runtime_config_key(key):
# matches common NUXT_ runtime-config override form without public/private suffixes
return 'NUXT_' + key.upper().replace('-', '_')
print('expected runtime override:', resolve_runtime_config_key('stripeSecretKey'))
print('STRIPE_SECRET_KEY used at runtime?:', bool(re.search(r"process\.env\[['\"]STRIPE_SECRET_KEY['\"]", validate + stripe)))
print('NUXT_STRIPE_SECRET_KEY used in validation error?:', 'NUXT_STRIPE_SECRET_KEY' in validate)
PYRepository: traPtitech/Checkin
Length of output: 384
STRIPE_SECRET_KEY の設定読み上げを修正してください。
nuxt.config.ts での stripeSecretKey 評価時に process.env['STRIPE_SECRET_KEY'] を参照していますが、ビルド後の実行プロセスへの環境変数を想定するなら、サーバー側の共通 resolver でその値を実行時に読み取って runtimeConfig.stripeSecretKey に渡してください。同じ設定を NUXT_STRIPE_SECRET_KEY で設定する形式も残す場合は、どちらを優先する README を統一してください。
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/nuxt.config.ts` around lines 8 - 9, Update the runtime configuration
flow around stripeSecretKey so the server-side shared resolver reads the secret
at application runtime and supplies it to runtimeConfig.stripeSecretKey instead
of evaluating process.env['STRIPE_SECRET_KEY'] during nuxt.config.ts build-time
loading. Preserve NUXT_STRIPE_SECRET_KEY support and document a single,
consistent precedence between the two environment variable forms in the README.
概要
Stripe を oRPC の契約ファースト API として公開する層を追加する。価格・商品・請求・Checkout の
4 リソースを、Nuxt の Nitro サーバーにマウントした oRPC ルーターとして実装する。契約
(
packages/api-contract)・実装(packages/api)・ホスト(apps/web/server)の 3 層に分け、クライアントはサーバー実装ではなく契約にリンクする。あわせて、各設計判断の理由を
openspec/project.mdの「規約」に文書化する。契約ファーストなので、
api-contract(契約)→api(ハンドラ・変換層)→apps/web/server(ホスト・起動時検証)の順に読むと差分を追いやすい。
設計方針(理由の詳細は
openspec/project.mdの「規約」)z.objectで明示列挙し、Stripe オブジェクトを透過しない。PII・内部フラグ・将来 Stripe が増やすフィールドを漏らさない。展開参照(customer/product 等)は
ID に正規化する。
expandは受け付けない(出力 allowlist を迂回してネストした metadata を漏らす経路になるため)。
変換層で対応づける。
{ data, nextCursor }。逆方向カーソルending_beforeは公開しない(カーソル導出戦略を、契約を変えずに差し替えられるようにするため)。(セッション・管理者依存エンドポイントの契約とバックエンドを実装する #18)。
価格や商品の変更)を塞ぐ(
mutationsEnabled/assertMutationsEnabled)。本来の認可は 認可・レート制限を導入し、無認証アクセスを塞ぐ #15。STRIPE_SECRET_KEYの存在を検証する(本番のみ、Stripe シークレットの存在を起動時に検証し、設定漏れで起動を失敗させる #23)。Stripe の
apiVersionを固定し、多段の請求フローは派生冪等キーでリトライ安全にする。現状の Stripe 系エンドポイントは Stripe を薄く包む管理・仲介層で、会員向けのドメイン API では
ない。会員向けはドメイン言語(会費・支払い状況)の別レイヤを設け、Stripe を裏に隠す(#27/#28)。
この PR で対応する issue
関連・フォローアップ
モデル・お金の記録・webhook 信頼性・非同期実行基盤・監査ログ・信頼境界 等)
テスト
冪等キーの派生・カーソル導出・変更系ガードを検証する。
pnpm typecheck/lint/knip/test/buildすべてグリーン。🤖 Generated with Claude Code
Summary by CodeRabbit
新機能
設定
変更