Skip to content

Stripeテスト環境セットアップ用Rakeタスクを追加#9845

Open
komagata wants to merge 13 commits intomainfrom
add-stripe-setup-rake-task
Open

Stripeテスト環境セットアップ用Rakeタスクを追加#9845
komagata wants to merge 13 commits intomainfrom
add-stripe-setup-rake-task

Conversation

@komagata
Copy link
Copy Markdown
Member

@komagata komagata commented Mar 28, 2026

概要

development環境でStripeテスト環境を実際に使えるようにするためのRakeタスク rake stripe:setup を追加します。

背景

fixtureのユーザーが持つ customer_idcus_12345678 等)がStripeテスト環境に実在しないため、development環境でStripe連携機能(退会、BillingPortal、カード変更など)をテストできませんでした。

変更内容

lib/tasks/stripe_setup.rake を追加:

  • customer_id を持つfixtureユーザーに対して、Stripeテスト環境に実際のCustomer/Subscriptionを作成
  • 既存のCustomerがあればそれを再利用(メールで検索)
  • DBの customer_id / subscription_id をStripeの実際のIDに更新
  • テストカード(tok_visa)で自動的にカード登録

使い方

bin/rails db:seed       # まずfixtureでDBを準備
bin/rails stripe:setup  # Stripeテスト環境と同期

特徴

  • 冪等: 何度実行しても安全(既存Customerを再利用)
  • development環境のみ: production環境では実行不可
  • 外部ツール不要: Stripe CLIなど不要、stripe gem のみ使用

Summary by CodeRabbit

  • 新機能

    • Stripeのテストリソース自動セットアップと、既存ユーザーのStripe顧客・サブスクリプションを一括で確認・同期する機能を追加しました。試用期間や税率の設定も考慮します。
  • 保守

    • dbシード時にStripeセットアップを条件付きで実行するようにし、テスト環境や不適切なAPIキーでの誤実行を防止します。
  • 削除

    • 旧来のStripeリセット用タスクを削除しました。

development環境でStripeテスト環境を実際に使えるようにするための
Rakeタスク `rake stripe:setup` を追加。

- customer_idを持つfixtureユーザーに対して、Stripeテスト環境に
  実際のCustomer/Subscriptionを作成(または既存を再利用)
- DBのcustomer_id/subscription_idをStripeと同期
- 冪等: 何度実行しても安全(メールで既存Customer検索→なければ作成)
- テストカード(tok_visa)で自動的にカード登録
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 28, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

新規 Rake タスク stripe:setup を追加し、Rails 環境と API キーをチェックして Stripe の Product・Plans・TaxRate を確保し、StripeSetup.sync_users で DB のユーザーを Stripe 顧客・サブスクリプションと同期します。bootcamp:reset_stripe タスクを削除しました。

Changes

Cohort / File(s) Summary
Stripe 設定タスク
lib/tasks/stripe.rake
新規 stripe:setup タスクと StripeSetup モジュールを追加。環境チェックの上で Stripe Product、standard/corporate Plan、JP TaxRate を検索または作成し、sync_users でユーザーの顧客・購読を Stripe と同期する関数群を実装。エラーは個別にハンドル。
シード処理統合
db/seeds.rb
Rake::Task.task_defined?('stripe:setup')StripeSetup.executable? を満たす場合に stripe:setup を呼び出す条件付きフックを追加。
レガシータスク削除
lib/tasks/bootcamp.rake
テスト用のハードコードされた Stripe 顧客/購読を作成していた bootcamp:reset_stripe タスクを削除。

Sequence Diagram(s)

sequenceDiagram
    participant Rake as Rake Task
    participant Setup as StripeSetup
    participant Stripe as Stripe API
    participant DB as Database
    participant AppConf as App Config

    Rake->>Setup: executable? -> ensure_resources()
    Setup->>Stripe: list/create Product
    Setup->>Stripe: list/create Plan (standard)
    Setup->>Stripe: list/create Plan (corporate)
    Setup->>Stripe: list/create TaxRate
    Setup->>AppConf: read stripe.tax_rate_id
    Setup->>DB: log or warn if mismatch

    Rake->>Setup: sync_users(resources)
    Setup->>DB: fetch Users with customer_id present
    loop per user
        Setup->>Stripe: find_or_create Customer (email, source, name)
        Setup->>Stripe: find_or_create Subscription (customer, plan, tax_rate, trial_end)
        Setup->>DB: update_columns(customer_id, subscription_id)
        Setup->>Rake: log success or error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 夜明けにタスクが跳ねる
商品並べて計画を立てるよ
顧客IDがぽんと増えて
古い鍵はそっとしまっておく
ピョン、同期の旅が始まった 🌙✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PRのタイトルは変更内容を明確に表現しており、新しいStripeセットアップ用Rakeタスクの追加を正確に説明しています。
Description check ✅ Passed PR説明は背景・目的・変更内容・使い方が記載されており、テンプレートの必須セクションである概要と変更確認方法がカバーされています。
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-stripe-setup-rake-task

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

- stripe-fixtures.json: Stripe CLIで `stripe fixtures` 実行用
  Product, スタンダードプラン, 法人研修プラン, Tax Rate を定義
- stripe_setup.rake: Rakeタスク内でもProduct/Plan/TaxRate の
  存在確認→なければ作成する処理を追加(stripe CLI不要でも動く)
- 新規Stripeテストアカウントでもゼロからセットアップ可能に
- Rakeタスクのガードを変更: development限定 → test環境とStripe本番キーを除外
  (stagingはRAILS_ENV=productionだがStripeテストキーを使うため実行可能)
- cloudbuild-staging.yamlにStripeSetupステップを追加
  db:seed後にstripe:setupを実行してCustomer/SubscriptionをStripeと同期
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 28, 2026

🚀 Review App

URL: https://bootcamp-pr-9845-fvlfu45apq-an.a.run.app

Basic認証: fjord / (ステージングと同じ)
PR更新時に自動で再デプロイされます。

- メソッド分割でMetrics/MethodLength, PerceivedComplexity解消
- StripeSetupモジュールに整理
- FloatComparison → to_d比較に変更
- StringLiterals → シングルクォート化
- update_columns → rubocop:disableコメント付与
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
lib/tasks/stripe.rake (1)

51-61: ページネーションの制限により既存リソースを見逃す可能性

limit: 100を使用していますが、Stripeアカウントに100件以上のリソースが存在する場合、既存リソースが見つからず重複作成される可能性があります。

開発/テスト環境では通常問題になりませんが、長期間使用されたStripeテストアカウントでは注意が必要です。

♻️ auto_paging_eachを使用した改善案
 def ensure_product
-  products = Stripe::Product.list(limit: 100).data
-  product = products.find { |p| p.name == 'フィヨルドブートキャンプ利用料' }
+  product = nil
+  Stripe::Product.list(limit: 100).auto_paging_each do |p|
+    if p.name == 'フィヨルドブートキャンプ利用料'
+      product = p
+      break
+    end
+  end
   if product
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/tasks/stripe.rake` around lines 51 - 61, The ensure_product method
currently calls Stripe::Product.list(limit: 100) which can miss matching
products if there are >100 entries; change the lookup to iterate all pages using
Stripe's auto-pagination (e.g., use Stripe::Product.list(...).auto_paging_each
or Stripe::Product.auto_paging_each where available) and search for the product
name ('フィヨルドブートキャンプ利用料') across all pages before creating a new product so you
never duplicate an existing product.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/tasks/stripe.rake`:
- Around line 139-150: The subscription is using a hardcoded tax_rate_id from
secrets instead of the tax rate object resolved by
ensure_tax_rate/ensure_resources, causing inconsistency; update
find_or_create_subscription to accept a tax_rate_id (or tax_rate object)
parameter and use that value when creating the Stripe::Subscription (replace the
Rails.application.config_for(:secrets) lookup), and propagate that argument from
sync_users/ensure_resources where you already obtain the canonical tax_rate so
the subscription creation uses the actual ID returned by ensure_tax_rate.

---

Nitpick comments:
In `@lib/tasks/stripe.rake`:
- Around line 51-61: The ensure_product method currently calls
Stripe::Product.list(limit: 100) which can miss matching products if there are
>100 entries; change the lookup to iterate all pages using Stripe's
auto-pagination (e.g., use Stripe::Product.list(...).auto_paging_each or
Stripe::Product.auto_paging_each where available) and search for the product
name ('フィヨルドブートキャンプ利用料') across all pages before creating a new product so you
never duplicate an existing product.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6ae8a48c-c6cf-49e6-9722-59eca1a403a5

📥 Commits

Reviewing files that changed from the base of the PR and between 635fd8c and d4c9e07.

📒 Files selected for processing (2)
  • .cloudbuild/cloudbuild-staging.yaml
  • lib/tasks/stripe.rake

- db/seeds.rb: seed完了後にstripe:setupを自動呼び出し(test環境除外)
  → bin/setup, db:reset, db:seed すべてでStripe同期が走る
- bootcamp.rake: 不完全だったbootcamp:reset_stripeタスクを削除
- cloudbuild-staging.yaml: StripeSetupステップを削除(seedで自動実行されるため不要)
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@db/seeds.rb`:
- Line 85: The guard around invoking Rake::Task['stripe:setup'] is too
broad—replace the current condition that checks !Rails.env.test? with an
explicit development-only check (Rails.env.development?) while still ensuring
Rake::Task.task_defined?('stripe:setup') is consulted before calling
Rake::Task['stripe:setup'].invoke; this limits execution to development and
avoids unintended seed aborts from the rake task.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0c5270ec-1ac6-4d79-a8f9-c34d6bbcc55b

📥 Commits

Reviewing files that changed from the base of the PR and between d4c9e07 and 760586a.

📒 Files selected for processing (2)
  • db/seeds.rb
  • lib/tasks/bootcamp.rake
💤 Files with no reviewable changes (1)
  • lib/tasks/bootcamp.rake

- ensure_resources → tax_rateを返すように変更
- sync_users → tax_rate引数を受け取り、find_or_create_subscriptionに渡す
- secrets.ymlの設定値ではなく、実際にStripeから取得/作成したtax_rateのIDを使用
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/tasks/stripe.rake`:
- Line 8: 現在のガード条件 uses Rails.env.production? so it won't stop live keys in
development; change the abort check around the abort call to unconditionally
abort when Stripe.api_key is nil or does not start_with?('sk_test_') (i.e.
remove the Rails.env.production? dependency) so the abort
'このタスクはStripeテスト環境でのみ実行できます…' triggers whenever Stripe.api_key.blank? or
!Stripe.api_key.start_with?('sk_test_'); reference the existing abort call and
Stripe.api_key to locate and update the guard.
- Around line 121-122: 既存顧客と購読を再利用する際に期待値と違っていたら修正する必要があります:
find_or_create_customer が既存顧客を返す前に支払い手段(例: tok_visa)が登録されているかチェックし、なければ Stripe
API を使って顧客に支払い手段を追加/アタッチしてから顧客を返すように修正してください。find_or_create_subscription は既存の
active/trialing subscription を見つけたら受け取った plan と tax_rate と照合し、不一致なら Stripe の
subscription 更新(items の価格変更、適切な tax_rates の差し替え)を行って期待状態(標準プラン +
消費税10%)に合わせてから返すように実装してください。IDempotency
を保つため、変更の前に現在の値を比較し、差分がある場合のみ更新するロジックを入れてください。
- Around line 95-102: The existing lookup that sets tax_rate via tax_rates.find
only checks display_name, percentage and active and must also match the create
parameters; update the find predicate (the block that assigns tax_rate from
tax_rates.find) to include t.inclusive == true and t.country == 'JP' so you only
reuse a TaxRate that matches display_name, percentage, active, inclusive and
country before falling back to Stripe::TaxRate.create with display_name: '消費税',
percentage: TAX_RATE_PERCENTAGE, inclusive: true, country: 'JP'.
- Around line 23-29: ensure_resources currently calls ensure_standard_plan but
doesn't return or propagate that plan; change ensure_standard_plan to return the
created/ensured Plan object (if it doesn't already), have ensure_resources
capture and return that standard_plan along with product/tax_rate (or return a
struct/tuple containing them), and update sync_users to accept a standard_plan
parameter instead of calling Plan.standard_plan; inside sync_users replace the
global search for existing plans with a lookup scoped to the given product (use
the product id/reference when querying plans) so the ensured plan is used
deterministically.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2fc6ed79-4daa-45de-9e72-3ea978496057

📥 Commits

Reviewing files that changed from the base of the PR and between 760586a and 47a519d.

📒 Files selected for processing (1)
  • lib/tasks/stripe.rake

- liveキーガードを環境非依存に変更(sk_test_以外は常にabort)
- ensure_resourcesからplan+tax_rateを返し、sync_usersに渡す(Plan.standard_plan再取得を廃止)
- TaxRate検索条件にinclusive/country追加(同名の別税率との誤一致を防止)
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
lib/tasks/stripe.rake (1)

63-90: ⚠️ Potential issue | 🟠 Major

既存 Plan の再利用条件は nickname だけでは足りません。

Line 65Line 80 は同名 Plan を別 product から拾えるうえ、金額や active 状態も見ていません。ここで外した Plan を sync_users に流すと、fixture ユーザーが意図しない料金体系に紐づきます。Stripe は Plan.listproduct / active を絞り込めるので、少なくとも product.idamountcurrencyinterval(標準プランは必要なら trial_period_days も)を作成条件に揃えて判定した方が安全です。 (docs.stripe.com)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/tasks/stripe.rake` around lines 63 - 90, The current Plan-reuse logic in
ensure_standard_plan and ensure_corporate_plan only matches by nickname which
can return a Plan from another product or with different pricing/active state;
change the logic to first call Stripe::Plan.list with filters product:
product.id and active: true (so the API narrows results) and then match returned
plans by amount, currency, interval (and for the standard plan also
trial_period_days when relevant) in addition to nickname before reusing; if no
exact match is found, create the Plan as before and return it.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@lib/tasks/stripe.rake`:
- Around line 63-90: The current Plan-reuse logic in ensure_standard_plan and
ensure_corporate_plan only matches by nickname which can return a Plan from
another product or with different pricing/active state; change the logic to
first call Stripe::Plan.list with filters product: product.id and active: true
(so the API narrows results) and then match returned plans by amount, currency,
interval (and for the standard plan also trial_period_days when relevant) in
addition to nickname before reusing; if no exact match is found, create the Plan
as before and return it.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 72d0a929-b966-496c-a7d4-ffa581f185dd

📥 Commits

Reviewing files that changed from the base of the PR and between 47a519d and e721f1d.

📒 Files selected for processing (1)
  • lib/tasks/stripe.rake

- StripeSetup.executable?: test環境ではfalse、sk_test_キーがある場合のみtrue
- db/seeds.rbの条件を !Rails.env.test? から StripeSetup.executable? に変更
  → staging環境(RAILS_ENV=production + テストキー)でも動く意図が明確に
- StripeSetup.executable?: Stripeキーベース → DB_NAME + Rails.envベースに変更
- staging?: ENV['DB_NAME'] == 'bootcamp_staging' で判定
  (bootcamp.rakeの既存パターンと統一)
- development? || staging? で実行可否を判断
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
lib/tasks/stripe.rake (1)

73-82: ⚠️ Potential issue | 🟠 Major

既存 Plan の再利用条件がまだ弱いです。

ここは nickname だけで既存 plan を返しているので、共有の Stripe テストアカウントに同名 plan が残っていると別 product の plan を再利用します。少なくとも product は一致確認し、できれば金額・interval・trial 条件まで作成条件と揃えた方が安全です。

🔧 修正案
-    standard = plans.find { |p| p.nickname == 'スタンダードプラン' }
+    standard = plans.find do |p|
+      p.product == product.id &&
+        p.nickname == 'スタンダードプラン' &&
+        p.amount == 29_800 &&
+        p.currency == 'jpy' &&
+        p.interval == 'month' &&
+        p.trial_period_days == 3
+    end
-    corporate = plans.find { |p| p.nickname == '法人研修プラン' }
+    corporate = plans.find do |p|
+      p.product == product.id &&
+        p.nickname == '法人研修プラン' &&
+        p.amount == 99_800 &&
+        p.currency == 'jpy' &&
+        p.interval == 'month'
+    end

Also applies to: 88-97

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/tasks/stripe.rake` around lines 73 - 82, The lookup for existing plans in
ensure_standard_plan is too permissive (only matches nickname) and can return a
plan belonging to another product; update the find predicate to ensure
p.nickname == 'スタンダードプラン' AND p.product (or p.product.id) equals product.id and
also verify p.amount == 29_800, p.interval == 'month', and p.trial_period_days
== 3 so the plan fully matches the create parameters; apply the same stricter
matching fix to the corresponding other method around lines 88-97 that
creates/checks the other plan.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/tasks/stripe.rake`:
- Around line 5-7: The rake task stripe:setup currently performs its own
environment and API-key checks instead of reusing StripeSetup.executable?,
causing inconsistent allow rules; update StripeSetup.executable? to encompass
both the Rails.env.test? guard (return false in test) and the Stripe.api_key
check (ensure it starts_with?('sk_test_')), then remove the inline aborts in the
task and replace them with a single conditional that aborts unless
StripeSetup.executable? (and use a clear single abort message), so both direct
runs and seed-invoked runs use the same executable? logic; reference: task name
"setup" in stripe:setup, StripeSetup.executable?, and Stripe.api_key.

---

Duplicate comments:
In `@lib/tasks/stripe.rake`:
- Around line 73-82: The lookup for existing plans in ensure_standard_plan is
too permissive (only matches nickname) and can return a plan belonging to
another product; update the find predicate to ensure p.nickname == 'スタンダードプラン'
AND p.product (or p.product.id) equals product.id and also verify p.amount ==
29_800, p.interval == 'month', and p.trial_period_days == 3 so the plan fully
matches the create parameters; apply the same stricter matching fix to the
corresponding other method around lines 88-97 that creates/checks the other
plan.
🪄 Autofix (Beta)

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c895f625-b8d9-4839-9f90-4de9dfdef038

📥 Commits

Reviewing files that changed from the base of the PR and between e721f1d and 3c2ee52.

📒 Files selected for processing (2)
  • db/seeds.rb
  • lib/tasks/stripe.rake
✅ Files skipped from review due to trivial changes (1)
  • db/seeds.rb

- lib/stripe_setup.rb: モジュール本体(ロジック全部)
- lib/tasks/stripe.rake: タスク定義のみ(require + 呼び出し)
- db/seeds.rb: require_relativeでStripeSetupを読み込み
- StripeSetup.executable?: test環境チェック + sk_test_キーチェック + 環境判定を一元化
- stripe.rake: 個別のabortを削除し、StripeSetup.executable?のみで判定
- seeds.rbとrakeタスクで同じexecutable?ロジックを共有
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant