Skip to content

Conversation

@Brendonovich
Copy link
Contributor

@Brendonovich Brendonovich commented Oct 15, 2025

Summary by CodeRabbit

  • New Features

    • Unified Stripe plan context for consistent pricing and checkout across the app.
    • Upgrade modal now appears only when enabled by configuration.
  • Refactor

    • Checkout and upgrade flows moved to mutation-driven patterns with clearer loading states and centralized error handling.
    • Plan IDs sourced from a shared configuration for predictable plan selection.
  • Chores

    • Consolidated Stripe environment variables and webhook secret handling for simplified configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 15, 2025

Walkthrough

Adds a React StripeContext providing plan IDs, integrates it into app layout, refactors checkout/upgrade flows to use React Query mutations and the new context, consolidates Stripe env vars to unified keys, and uses a single webhook secret source for Stripe webhook verification.

Changes

Cohort / File(s) Summary
Stripe context & provider
apps/web/app/Layout/StripeContext.tsx, apps/web/app/layout.tsx
New StripeContext and StripeContextProvider; selects plan IDs from STRIPE_PLAN_IDS (via serverEnv()/buildEnv) and exposes useStripeContext; layout now wraps app with provider.
Upgrade & pricing components (mutations + context)
apps/web/components/UpgradeModal.tsx, apps/web/components/pages/HomePage/Pricing/ProCard.tsx, apps/web/components/pages/_components/ComparePlans.tsx
Replace ad-hoc fetch flows with React Query useMutation (guestCheckout/planCheckout); resolve plan IDs from useStripeContext; remove getProPlanId usage; UpgradeModal now conditionally exports a no-op when cap flag is off.
Env schema consolidation
packages/env/server.ts
Replace per-environment Stripe keys/secrets with unified STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET declarations.
Plan constants refactor
packages/utils/src/constants/plans.ts
Add exported STRIPE_PLAN_IDS map and remove getProPlanId and NODE_ENV branching; plan IDs accessed from static map.
Stripe server util & webhook behavior
packages/utils/src/lib/stripe/stripe.ts, apps/web/app/api/webhooks/stripe/route.ts
stripe.ts: use STRIPE_SECRET_KEY ?? "" for key lookup; webhook route now always reads serverEnv().STRIPE_WEBHOOK_SECRET (no env-branching) for signature verification.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as Client UI
  participant Ctx as StripeContext
  participant API as /api/billing/subscribe
  participant Auth as Auth Service
  participant Stripe as Stripe Checkout

  User->>UI: Click "Upgrade"
  UI->>Ctx: Read planId (yearly/monthly)
  UI->>API: planCheckout.mutate(planId)
  API->>Auth: Verify session
  alt Not authenticated
    API-->>UI: { auth: false }
    UI->>API: guestCheckout.mutate(planId)
    API-->>UI: { url }
    UI->>User: Redirect to url
  else Authenticated
    API-->>UI: { status, url? }
    alt url provided
      UI->>User: Redirect to url (Stripe)
    else
      UI-->>User: Show subscription status message
    end
  end
Loading
sequenceDiagram
  autonumber
  participant Stripe as Stripe
  participant Webhook as /api/webhooks/stripe
  participant Env as serverEnv
  participant Handler as Event Handler

  Stripe->>Webhook: POST (payload + signature)
  Webhook->>Env: Read STRIPE_WEBHOOK_SECRET
  Webhook->>Webhook: Verify signature with secret
  alt valid
    Webhook->>Handler: process event
    Handler-->>Webhook: processed
    Webhook-->>Stripe: 200 OK
  else invalid
    Webhook-->>Stripe: 400/401 Error
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • improvement: enterprise card #1046 — Modifies apps/web/components/pages/HomePage/Pricing/ProCard.tsx; likely overlaps with mutation and UI changes.
  • New pricing updates #980 — Updates plan ID data in packages/utils/src/constants/plans.ts; may conflict with the introduced STRIPE_PLAN_IDS.
  • last few staging things #1190 — Changes webhook secret selection in apps/web/app/api/webhooks/stripe/route.ts; strongly related to webhook secret sourcing change.

Poem

A rabbit taps the checkout key,
Plans in context, hopping free,
Mutations scurry, loading light,
One secret kept, webhooks right.
Thump-thump—ship it through the night! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly highlights the two primary changes introduced by the pull request: unifying the Stripe environment variables and improving the differentiation of Stripe plans, which aligns directly with the core modifications to env handling and plan context logic. It is concise, specific, and avoids unnecessary noise. A reviewer scanning the title would immediately understand the scope of the refactor.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-stripe-envs

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.

Copy link
Contributor

@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: 3

🧹 Nitpick comments (1)
apps/web/components/pages/HomePage/Pricing/ProCard.tsx (1)

65-91: Consider extracting nested mutation logic.

The plan checkout mutation works correctly, but calling guestCheckout.mutateAsync inside the planCheckout mutation creates coupling between the two mutations.

Consider extracting the authentication check and routing logic into a helper function or composing the mutations differently to improve testability and separation of concerns. The current implementation is functional but could be cleaner.

Example refactor:

const planCheckout = useMutation({
  mutationFn: async () => {
    const planId = stripeCtx.plans[isAnnually ? "yearly" : "monthly"];
    
    const response = await fetch(`/api/settings/billing/subscribe`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ priceId: planId, quantity: users }),
    });
    return { data: await response.json(), planId };
  },
  onSuccess: async ({ data, planId }) => {
    if (data.auth === false) {
      await guestCheckout.mutateAsync(planId);
      return;
    }
    
    if (data.subscription === true) {
      toast.success("You are already on the Cap Pro plan");
    }
    
    if (data.url) {
      window.location.href = data.url;
    }
  },
});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d35c189 and a20aab2.

📒 Files selected for processing (9)
  • apps/web/app/Layout/StripeContext.tsx (1 hunks)
  • apps/web/app/api/webhooks/stripe/route.ts (1 hunks)
  • apps/web/app/layout.tsx (3 hunks)
  • apps/web/components/UpgradeModal.tsx (5 hunks)
  • apps/web/components/pages/HomePage/Pricing/ProCard.tsx (4 hunks)
  • apps/web/components/pages/_components/ComparePlans.tsx (4 hunks)
  • packages/env/server.ts (1 hunks)
  • packages/utils/src/constants/plans.ts (1 hunks)
  • packages/utils/src/lib/stripe/stripe.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Use strict TypeScript and avoid any; leverage shared types

Files:

  • packages/utils/src/lib/stripe/stripe.ts
  • apps/web/app/layout.tsx
  • apps/web/app/api/webhooks/stripe/route.ts
  • apps/web/components/pages/HomePage/Pricing/ProCard.tsx
  • packages/env/server.ts
  • packages/utils/src/constants/plans.ts
  • apps/web/app/Layout/StripeContext.tsx
  • apps/web/components/pages/_components/ComparePlans.tsx
  • apps/web/components/UpgradeModal.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • packages/utils/src/lib/stripe/stripe.ts
  • apps/web/app/layout.tsx
  • apps/web/app/api/webhooks/stripe/route.ts
  • apps/web/components/pages/HomePage/Pricing/ProCard.tsx
  • packages/env/server.ts
  • packages/utils/src/constants/plans.ts
  • apps/web/app/Layout/StripeContext.tsx
  • apps/web/components/pages/_components/ComparePlans.tsx
  • apps/web/components/UpgradeModal.tsx
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/app/layout.tsx
  • apps/web/app/api/webhooks/stripe/route.ts
  • apps/web/components/pages/HomePage/Pricing/ProCard.tsx
  • apps/web/app/Layout/StripeContext.tsx
  • apps/web/components/pages/_components/ComparePlans.tsx
  • apps/web/components/UpgradeModal.tsx
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and fetching in the web app
Mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData
Run server-side effects via the ManagedRuntime from apps/web/lib/server.ts using EffectRuntime.runPromise/runPromiseExit; do not create runtimes ad hoc
Client code should use helpers from apps/web/lib/EffectRuntime.ts (useEffectQuery, useEffectMutation, useRpcClient); never call ManagedRuntime.make inside components

Files:

  • apps/web/app/layout.tsx
  • apps/web/app/api/webhooks/stripe/route.ts
  • apps/web/components/pages/HomePage/Pricing/ProCard.tsx
  • apps/web/app/Layout/StripeContext.tsx
  • apps/web/components/pages/_components/ComparePlans.tsx
  • apps/web/components/UpgradeModal.tsx
apps/web/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Server components needing Effect services must call EffectRuntime.runPromise(effect.pipe(provideOptionalAuth))

Files:

  • apps/web/app/layout.tsx
  • apps/web/app/api/webhooks/stripe/route.ts
  • apps/web/app/Layout/StripeContext.tsx
apps/web/app/api/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/app/api/**/*.{ts,tsx}: Prefer Server Actions for API surface; when routes are necessary, implement under app/api and export only the handler from apiToHandler(ApiLive)
Construct API routes with @effect/platform HttpApi/HttpApiBuilder, declare contracts with Schema, and only export the handler
Use HttpAuthMiddleware for required auth and provideOptionalAuth for guests; avoid duplicating session lookups
Map domain errors to transport with HttpApiError.* and keep translation exhaustive (catchTags/tapErrorCause)
Inside HttpApiBuilder.group, acquire services with Effect.gen and provide dependencies via Layer.provide instead of manual provideService

Files:

  • apps/web/app/api/webhooks/stripe/route.ts
🧬 Code graph analysis (6)
packages/utils/src/lib/stripe/stripe.ts (1)
packages/env/server.ts (1)
  • serverEnv (82-86)
apps/web/app/layout.tsx (4)
apps/web/app/Layout/StripeContext.tsx (1)
  • StripeContextProvider (8-17)
packages/env/server.ts (1)
  • serverEnv (82-86)
packages/utils/src/constants/plans.ts (1)
  • STRIPE_PLAN_IDS (3-12)
apps/web/utils/public-env.tsx (1)
  • PublicEnvContext (11-17)
apps/web/app/api/webhooks/stripe/route.ts (1)
packages/env/server.ts (1)
  • serverEnv (82-86)
apps/web/components/pages/HomePage/Pricing/ProCard.tsx (1)
apps/web/app/Layout/StripeContext.tsx (1)
  • useStripeContext (19-27)
apps/web/components/pages/_components/ComparePlans.tsx (1)
apps/web/app/Layout/StripeContext.tsx (1)
  • useStripeContext (19-27)
apps/web/components/UpgradeModal.tsx (1)
apps/web/app/Layout/StripeContext.tsx (1)
  • useStripeContext (19-27)
🔇 Additional comments (16)
packages/utils/src/lib/stripe/stripe.ts (1)

4-4: LGTM! Simplified key resolution.

The change from a fallback chain to a single STRIPE_SECRET_KEY lookup simplifies the logic and aligns with the environment consolidation. The fallback to empty string preserves the existing STRIPE_AVAILABLE() behavior.

packages/env/server.ts (1)

40-41: LGTM! Consolidated Stripe environment variables.

Consolidating from four environment-specific keys to two unified keys (STRIPE_SECRET_KEY and STRIPE_WEBHOOK_SECRET) simplifies configuration and reduces potential for misconfiguration.

apps/web/app/api/webhooks/stripe/route.ts (1)

118-118: LGTM! Unified webhook secret source.

The change to a single STRIPE_WEBHOOK_SECRET source simplifies the webhook verification logic and aligns with the environment consolidation.

Ensure that STRIPE_WEBHOOK_SECRET is properly configured in all deployment environments (development, staging, production) with the appropriate webhook signing secrets from your Stripe dashboard.

apps/web/components/pages/_components/ComparePlans.tsx (3)

16-16: LGTM! Added Stripe context integration.

Importing useStripeContext enables context-based plan ID resolution, aligning with the new centralized approach.


97-97: LGTM! Stripe context consumption.

Calling useStripeContext() provides access to centralized plan IDs, replacing the previous getProPlanId helper.


252-252: LGTM! Context-based default plan ID.

Using stripeCtx.plans.yearly as the default plan ID is a sensible default and aligns with the new centralized configuration.

apps/web/app/layout.tsx (2)

24-25: LGTM! Added Stripe context imports.

Importing STRIPE_PLAN_IDS and StripeContextProvider enables centralized plan ID management throughout the app.


116-137: Confirm VERCEL_ENV fallback and preview behavior
Locally VERCEL_ENV is unset, so the code defaults to development plans. Verify that on Vercel preview deployments VERCEL_ENV="preview" and that using the development plan in both preview and local is intended. If you need a distinct plan for preview, adjust this conditional.

apps/web/app/Layout/StripeContext.tsx (2)

5-6: LGTM! Clear context type definition.

The StripeContext type clearly defines the expected structure with yearly and monthly plan IDs.


8-17: LGTM! Standard provider pattern.

The provider implementation follows React best practices, accepting both children and plans through props, and conditionally providing context value.

apps/web/components/pages/HomePage/Pricing/ProCard.tsx (5)

18-19: LGTM! Added necessary imports for context and mutations.

Importing useStripeContext and useMutation enables the migration to context-based plan IDs and mutation-driven billing flows.


22-22: LGTM! Stripe context consumption.

Calling useStripeContext() provides access to centralized plan IDs.


43-63: LGTM! Guest checkout as mutation.

Converting guestCheckout to a useMutation hook provides better loading state management and error handling compared to the previous ad-hoc async function.


300-301: LGTM! Mutation-based loading state.

Using planCheckout.isPending || guestCheckout.isPending for the disabled state correctly reflects the loading status of either mutation.


305-307: LGTM! Mutation-based button text.

Conditionally rendering "Loading..." based on mutation pending state provides clear user feedback during checkout operations.

packages/utils/src/constants/plans.ts (1)

3-12: Verification needed: Confirm Stripe price IDs
The script returned “NOT FOUND” for all IDs—ensure STRIPE_SECRET_KEY is set correctly and rerun, or verify these IDs directly in your Stripe Dashboard or via the Stripe CLI.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@Brendonovich Brendonovich merged commit 671903c into main Oct 15, 2025
13 of 15 checks passed
Copy link
Contributor

@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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/components/pages/HomePage/Pricing/ProCard.tsx (1)

43-91: Use useEffectMutation instead of useMutation.

Per coding guidelines for apps/web/**/*.{ts,tsx,js,jsx}, client components must use useEffectMutation from @/lib/EffectRuntime rather than useMutation directly. This ensures execution within the managed runtime and consistent error handling.

Based on learnings

Refactor both mutations to use useEffectMutation:

-import { useMutation } from "@tanstack/react-query";
+import { useEffectMutation } from "@/lib/EffectRuntime";

-const guestCheckout = useMutation({
+const guestCheckout = useEffectMutation({
   mutationFn: async (planId: string) => {
     // ... existing logic
   },
   onError: () => {
     toast.error("An error occurred. Please try again.");
   },
 });

-const planCheckout = useMutation({
+const planCheckout = useEffectMutation({
   mutationFn: async () => {
     // ... existing logic
   },
 });
♻️ Duplicate comments (2)
apps/web/components/UpgradeModal.tsx (2)

134-163: Use useEffectMutation instead of useMutation.

Per coding guidelines for apps/web/**/*.{ts,tsx,js,jsx}, client components must use useEffectMutation from @/lib/EffectRuntime rather than useMutation directly.

Based on learnings

-import { useMutation } from "@tanstack/react-query";
+import { useEffectMutation } from "@/lib/EffectRuntime";

-const planCheckout = useMutation({
+const planCheckout = useEffectMutation({
   mutationFn: async () => {
     // ... existing logic
   },
 });

306-307: Fix the conditional export to preserve TypeScript props.

The conditional export () => null drops the UpgradeModalProps signature, breaking type checking at call sites. Type the disabled branch to accept props or explicitly annotate the export.

 export const UpgradeModal =
-	buildEnv.NEXT_PUBLIC_IS_CAP !== "true" ? () => null : UpgradeModalImpl;
+	buildEnv.NEXT_PUBLIC_IS_CAP !== "true"
+		? (_props: UpgradeModalProps) => null
+		: UpgradeModalImpl;
🧹 Nitpick comments (3)
apps/web/app/Layout/StripeContext.tsx (1)

8-17: Consider making plans required instead of Partial.

The provider accepts Partial<StripeContext> but the hook throws an error if context is undefined, effectively making plans a required prop. Since all usage sites provide plans (as seen in layout.tsx), consider simplifying the type to make this requirement explicit:

 export function StripeContextProvider({
 	children,
 	plans,
-}: PropsWithChildren & Partial<StripeContext>) {
+}: PropsWithChildren & StripeContext) {
 	return (
-		<StripeContext.Provider value={plans ? { plans } : undefined}>
+		<StripeContext.Provider value={{ plans }}>
 			{children}
 		</StripeContext.Provider>
 	);
 }
apps/web/components/pages/HomePage/Pricing/ProCard.tsx (1)

65-91: Add error handling to planCheckout mutation.

The guestCheckout mutation includes an onError handler, but planCheckout does not. Consider adding consistent error handling to provide better user feedback when the subscription flow fails.

 const planCheckout = useEffectMutation({
   mutationFn: async () => {
     // ... existing logic
   },
+  onError: () => {
+    toast.error("Failed to process subscription. Please try again.");
+  },
 });
apps/web/components/UpgradeModal.tsx (1)

134-163: Add error handling to the mutation.

The mutation lacks error handling. Consider adding an onError callback to provide user feedback when the upgrade flow fails.

 const planCheckout = useEffectMutation({
   mutationFn: async () => {
     // ... existing logic
   },
+  onError: () => {
+    toast.error("Failed to process upgrade. Please try again.");
+  },
 });
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a20aab2 and 70be489.

📒 Files selected for processing (5)
  • apps/web/app/Layout/StripeContext.tsx (1 hunks)
  • apps/web/app/layout.tsx (3 hunks)
  • apps/web/components/UpgradeModal.tsx (6 hunks)
  • apps/web/components/pages/HomePage/Pricing/ProCard.tsx (4 hunks)
  • apps/web/components/pages/_components/ComparePlans.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/components/pages/_components/ComparePlans.tsx
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx}: Use a 2-space indent for TypeScript code.
Use Biome for formatting and linting TypeScript/JavaScript files by running pnpm format.

Use strict TypeScript and avoid any; leverage shared types

Files:

  • apps/web/components/pages/HomePage/Pricing/ProCard.tsx
  • apps/web/app/layout.tsx
  • apps/web/app/Layout/StripeContext.tsx
  • apps/web/components/UpgradeModal.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use kebab-case for filenames for TypeScript/JavaScript modules (e.g., user-menu.tsx).
Use PascalCase for React/Solid components.

Files:

  • apps/web/components/pages/HomePage/Pricing/ProCard.tsx
  • apps/web/app/layout.tsx
  • apps/web/app/Layout/StripeContext.tsx
  • apps/web/components/UpgradeModal.tsx
apps/web/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

On the client, always use useEffectQuery or useEffectMutation from @/lib/EffectRuntime; never call EffectRuntime.run* directly in components.

Files:

  • apps/web/components/pages/HomePage/Pricing/ProCard.tsx
  • apps/web/app/layout.tsx
  • apps/web/app/Layout/StripeContext.tsx
  • apps/web/components/UpgradeModal.tsx
apps/web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

apps/web/**/*.{ts,tsx}: Use TanStack Query v5 for all client-side server state and fetching in the web app
Mutations should call Server Actions directly and perform targeted cache updates with setQueryData/setQueriesData
Run server-side effects via the ManagedRuntime from apps/web/lib/server.ts using EffectRuntime.runPromise/runPromiseExit; do not create runtimes ad hoc
Client code should use helpers from apps/web/lib/EffectRuntime.ts (useEffectQuery, useEffectMutation, useRpcClient); never call ManagedRuntime.make inside components

Files:

  • apps/web/components/pages/HomePage/Pricing/ProCard.tsx
  • apps/web/app/layout.tsx
  • apps/web/app/Layout/StripeContext.tsx
  • apps/web/components/UpgradeModal.tsx
apps/web/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Server components needing Effect services must call EffectRuntime.runPromise(effect.pipe(provideOptionalAuth))

Files:

  • apps/web/app/layout.tsx
  • apps/web/app/Layout/StripeContext.tsx
🧠 Learnings (2)
📚 Learning: 2025-09-22T14:19:56.010Z
Learnt from: CR
PR: CapSoftware/Cap#0
File: AGENTS.md:0-0
Timestamp: 2025-09-22T14:19:56.010Z
Learning: Applies to apps/web/**/*.{ts,tsx,js,jsx} : On the client, always use `useEffectQuery` or `useEffectMutation` from `@/lib/EffectRuntime`; never call `EffectRuntime.run*` directly in components.

Applied to files:

  • apps/web/components/UpgradeModal.tsx
📚 Learning: 2025-10-14T10:15:44.003Z
Learnt from: CR
PR: CapSoftware/Cap#0
File: CLAUDE.md:0-0
Timestamp: 2025-10-14T10:15:44.003Z
Learning: Applies to apps/web/**/*.{ts,tsx} : Client code should use helpers from apps/web/lib/EffectRuntime.ts (useEffectQuery, useEffectMutation, useRpcClient); never call ManagedRuntime.make inside components

Applied to files:

  • apps/web/components/UpgradeModal.tsx
🧬 Code graph analysis (3)
apps/web/components/pages/HomePage/Pricing/ProCard.tsx (1)
apps/web/app/Layout/StripeContext.tsx (1)
  • useStripeContext (19-27)
apps/web/app/layout.tsx (4)
apps/web/app/Layout/StripeContext.tsx (1)
  • StripeContextProvider (8-17)
packages/env/server.ts (1)
  • serverEnv (82-86)
packages/utils/src/constants/plans.ts (1)
  • STRIPE_PLAN_IDS (3-12)
apps/web/utils/public-env.tsx (1)
  • PublicEnvContext (11-17)
apps/web/components/UpgradeModal.tsx (2)
apps/web/app/Layout/StripeContext.tsx (1)
  • useStripeContext (19-27)
packages/env/build.ts (1)
  • buildEnv (33-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (4)
apps/web/app/layout.tsx (1)

116-137: LGTM! Provider hierarchy correctly structured.

The StripeContextProvider integration is well-implemented:

  • Plans are correctly selected based on serverEnv().VERCEL_ENV
  • Provider nesting allows child components to access Stripe context
  • Server-side plan resolution is appropriate for this layout component
apps/web/app/Layout/StripeContext.tsx (1)

19-27: LGTM! Hook implementation is correct.

The error handling and use of React's use API is appropriate for React 19.

apps/web/components/pages/HomePage/Pricing/ProCard.tsx (1)

67-67: LGTM! Plan ID resolution using StripeContext.

The switch from getProPlanId to stripeCtx.plans centralizes plan resolution and aligns with the broader refactoring.

apps/web/components/UpgradeModal.tsx (1)

136-136: LGTM! Plan ID resolution using StripeContext.

The centralized plan resolution through stripeCtx.plans is consistent with the broader refactoring effort.

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.

2 participants