Skip to content

[Release] Pro / 広告リリース(2026-05-31) - #313

Draft
ippei-shimizu wants to merge 20 commits into
stgfrom
release/pro-202605
Draft

[Release] Pro / 広告リリース(2026-05-31)#313
ippei-shimizu wants to merge 20 commits into
stgfrom
release/pro-202605

Conversation

@ippei-shimizu

Copy link
Copy Markdown
Owner

概要

BUZZ BASE Pro リリース(2026年5月31日予定)の統合リリースブランチ。
各機能の作業ブランチはこの release/pro-202605 から切り、このブランチへマージしていく。
本番反映時に stg へマージする。

含まれる issue(ルートリポジトリ: ippei-shimizu/buzzbase)

開発フロー

作業ブランチ(feature/{issue番号}-{機能名})→ release/pro-202605 → stg

マージ予定

2026-05-30 頃 → stg へマージ → 本番反映。
それまでは Draft 状態を維持。

関連ドキュメント(buzzbase ルートリポジトリ)

  • 戦略: docs/strategy/product/pro-plan-202605.md
  • PRD 親: docs/strategy/product/pro-plan-prd-202605.md

@ippei-shimizu ippei-shimizu self-assigned this May 16, 2026
@vercel

vercel Bot commented May 16, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
buzzbase-front Ready Ready Preview, Comment Jun 27, 2026 3:27am

ippei-shimizu and others added 10 commits May 17, 2026 16:09
- types/pro.ts: FREE_FEATURES / PRO_FEATURES 定数 (各キーに説明コメント) と
  ProStatus / ProSubscription 等の型定義
- (app)/pro/actions.ts: getProStatus / getEntitlements / syncProStatus
  (Cookie 認証ヘッダを Server Action で構築)

back/Entitlement モジュールと feature key を一致させる必要があるため、
キー名と説明は back/app/models/concerns/entitlement.rb と同期する。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- ProStatusProvider: Server Component から初期値を受け取り Context で配下に配布
  - useTransition + refresh() で Server Action 再呼び出しによる再取得を提供
  - Provider が無い階層では DEFAULT_PRO_STATUS (無料状態) に fallback
- useProStatus: Context 経由で Pro 状態と isPro / refresh を取得
- useEntitlement: hasEntitlement(feature) を提供。back の Entitlement#has_entitlement? と
  同じ判定ロジックをクライアント側で表現する

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- ProGate: feature を渡すと entitlement を判定し、許可時のみ children を表示
  - fallback: 未許可時の代替表示 (静的)
  - renderLockedTrigger: タップで PaywallModal を開くトリガー UI を提供する関数
- PaywallModal: Pro 加入を促す HeroUI ベースのモーダル
  - PRO_PAYWALL_COPY で feature ごとの訴求コピーを定義
  - 'Pro プランを見る' で /pro へ遷移、'閉じる' で onClose

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- useEntitlement: Provider 有無 / 無料 / Pro / トライアル / グレースピリオドの各状態で
  hasEntitlement と派生フラグが正しく返ることを検証
- ProGate: 許可時は children、未許可時は fallback、renderLockedTrigger で
  PaywallModal が開く挙動を確認
- PaywallModal: feature ごとのコピー表示と Pro プラン遷移 / 閉じる挙動を確認

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
back の SubscriptionSerializer に pro_active が追加されたのを受け、
クライアント側の isPro 判定を 'status 文字列マッチ' から 'pro_active を読む' に変更する。

これにより:
- 期限切れの cancelled / billing_issue で誤って isPro=true になるバグを回避
- back と front の判定ロジックが完全に揃い、片方だけ仕様変更したときの
  乖離リスクが消える

ProSubscription 型に pro_active を追加し、DEFAULT_PRO_STATUS とテスト fixture も
連動して更新。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
string[] のままだと back の Entitlement 定数と乖離した値が紛れ込んでも
コンパイル時に検出できない。Feature ユニオン型に絞ることで back のキー追加・
削除に追従しやすくする。

DEFAULT_PRO_STATUS の初期化も Feature[] アサーションで揃える。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
renderLockedTrigger も fallback も無いケースで PaywallModal が isOpen=false のまま
常に DOM に残っていた。タップで開く手段が無いのに残しておく理由がないため、
renderLockedTrigger があるときだけ PaywallModal をレンダリングする形に変更。

bundle 含有ロジックを単純化し、未許可時の挙動が3パターン
(renderLockedTrigger / fallback / なし) に明示的になる。

テストも、未許可かつ trigger 無しのケースで Paywall コピーが描画されないことを
追加で検証する。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
router.push だと Next.js の prefetch が効かないため、HeroUI Button の as={Link} と
href='/pro' で Next.js の Link を埋め込み、課金導線のページを事前 prefetch できるようにする。

副次効果として useRouter への依存が消え、Server Component 化や Storybook 等への
取り回しがしやすくなる。

テストは router モックを撤去し、ボタン内側の <a href> を closest('a') で検証する
振る舞いベースの形に書き換える。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
POST リクエストは Next.js のフェッチキャッシュの対象外で、no-store は GET 向けの
オプションのため挙動を変えない。誤解を招くだけなのでコメント付きで削除する。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…aywall

Add: Pro Gate / Paywall コンポーネント (#317)
ippei-shimizu and others added 2 commits May 23, 2026 21:41
…-release-pro-202605

Chore: stg を release/pro-202605 に取り込み (#344)
ippei-shimizu and others added 2 commits May 24, 2026 09:21
release/pro-202605 等のリリースブランチ上での push / PR でも CI を走らせる。
…ch-in-ci

Chore: CI workflow の対象ブランチに release/* を追加
ippei-shimizu and others added 4 commits May 25, 2026 23:33
Web 経由の Pro 加入導線を実装する。back の POST /api/v1/pro/checkout を
Server Action 経由で呼び、返却された checkout_url にブラウザを遷移させる。

## 追加ライブラリ
- @stripe/stripe-js(将来 redirectToCheckout 等の利用に備え)

## 新規エンドポイント (front)
- /pro                — Pro LP(加入済みは /account/subscription へ redirect)
- /pro/success        — Stripe Checkout 成功戻り先
- /pro/cancel         — Stripe Checkout 中断戻り先

## LP セクション(_components 配下にコロケーション)
- HeroSection / PricingCards / FeatureComparisonTable
- Screenshots / Testimonials / FAQ / FinalCTA
- CheckoutButton(Client Component、Server Action 呼び出しのみ)

## Server Action
- startProCheckout({ plan, baseUrl })
  → back の checkout エンドポイントを叩き { ok: true, checkoutUrl } を返却
  → エラー時は discriminated union で error code を返却(既存 getProStatus パターンと一貫性)

## 設計判断
- baseUrl は CheckoutButton (Client) で window.location.origin を解決して
  Server Action に渡す(Server からはホスト推測しない)
- 価格は ¥980 / ¥9,800 で front ハードコード(Design Doc 確定済み)
- Screenshots / Testimonials は枠だけ用意、E1 でリアルテキスト/画像に差し替え

## テスト
- actions.test.ts: 200 / 401 / 409 / 422 invalid_plan / 502 stripe_api_error / unknown
- CheckoutButton.test.tsx: plan 引数 / エラー時 toast
- PricingCards.test.tsx: 両プラン表示・割引バッジ

Tests: 275 passed
Typecheck: 0 error
Lint: 0 warning
Pro 加入導線を「Web LP の直接 Checkout ボタン」「機能タップ時の PaywallModal」と
個別実装していたところ、参考画像で議論した結果 **共通モーダル 1 つに集約** する設計に変更。

## 設計変更
- /pro/upgrade ルートは作らない。モーダルメイン動線
- 既存 PaywallModal は廃止し ProUpgradeModal に統合
- LP /pro の CheckoutButton も直接 Stripe Checkout を呼ばず、モーダルを開く挙動に変更
- Desktop は中央モーダル max-w-2xl、Mobile は bottom sheet 風

## 新規ファイル
- app/contexts/proUpgradeModalContext.tsx — Provider + useProUpgradeModal()
- app/components/pro/ProUpgradeModal.tsx — 共通モーダル本体
  - 上部: trigger があれば機能別コピー(旧 PaywallModal の文言を流用)
  - 機能リスト
  - プラン Radio(年額おすすめ / 月額)
  - 注意事項
  - 固定 CTA(startProCheckout 呼び出し)

## 削除
- app/components/pro/PaywallModal.tsx
- app/components/pro/__tests__/PaywallModal.test.tsx

## 書き換え
- app/providers.tsx: ProUpgradeModalProvider を注入
- app/components/pro/ProGate.tsx: useProUpgradeModal().open(feature) で開く
- app/(app)/pro/_components/CheckoutButton.tsx: モーダルトリガー化
- app/(app)/pro/_components/PricingCards.tsx / FinalCTA.tsx: CheckoutButton 呼び出し変更
- 各テストを新仕様に追従

Tests: 272 passed
Typecheck: 0 error
Lint: 0 warning
## 🔴 baseUrl をクライアントから受け取るリスクを解消
Server Action は POST で直接叩けるため、攻撃者が baseUrl に任意ドメインを差し込めば
Stripe Checkout の success_url / cancel_url を攻撃者サイトに誘導でき、決済フィッシング
や session_id 漏洩につながる。
- startProCheckout 引数から baseUrl を削除
- サーバー側で process.env.APP_URL から構築(default: http://localhost:8100)
- 呼び出し元(ProUpgradeModal)から baseUrl 引数の渡しを除去
- actions.test.ts に APP_URL 未設定時のデフォルトケースも追加

## 🟡 PricingCards のカードごとに defaultPlan を引き継ぐ
月額カードをタップしても年額で開く UX ミスマッチを解消。
- Context の open() を { trigger?, defaultPlan? } options 形式に拡張
- CheckoutButton に defaultPlan?: ProPlan を追加
- PricingCards で月額/年額カードに対応する defaultPlan を渡す
- Provider 側で open ごとに key={openCount} を更新し ProUpgradeModal を remount
  → useEffect による派生 state 同期を避け、毎回 defaultPlan が初期値として再評価される

## 🟡 trigger 型を ProFeature に絞る + copy 導出を簡素化
- ProUpgradeModal の trigger 型を Feature → ProFeature
- copy 導出を const copy = (trigger && PRO_PAYWALL_COPY[trigger]) ?? DEFAULT_COPY に
- ProGate は Feature を受けるため PRO_FEATURES.includes() で判定して
  ProFeature のときだけ trigger 付きで open する

## 🟡 /pro/success と /pro/cancel に noindex を追加
robots: { index: false } を metadata に追加。トランザクションページの検索インデックス回避。

## 🟢 useMediaQuery に初期値 false を明示
ハイドレーション直後の placement フラッシュ(center → bottom)を防止。

## テスト追従
- actions.test.ts: baseUrl 引数削除、APP_URL 環境変数のセットアップ追加
- CheckoutButton.test.tsx: defaultPlan 伝播ケース追加
- ProGate.test.tsx: open 呼び出しが { trigger } object 形式になった追従

Tests: 274 passed
Typecheck: 0 error
Lint: 0 warning
Add: F1a — /pro リッチ LP + Stripe Checkout 連携 (#351)
CI workflow のブランチトリガー衝突を release/** 採用で解消(上位互換のため)。
他ファイルは自動マージ。typecheck / lint 通過済み。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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