Skip to content

45 set up stripe - #152

Closed
InfinityBowman wants to merge 3 commits into
mainfrom
45-set-up-stripe
Closed

45 set up stripe#152
InfinityBowman wants to merge 3 commits into
mainfrom
45-set-up-stripe

Conversation

@InfinityBowman

@InfinityBowman InfinityBowman commented Dec 25, 2025

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Integrated Stripe payment processing for subscriptions.
    • Added dedicated checkout page with payment form for selecting billing tiers.
    • Moved billing interface from /settings/billing to /billing.
    • Added automatic subscription tracking and status updates via webhooks.
  • Chores

    • Added Stripe JavaScript SDK dependency and MCP server configuration.

✏️ Tip: You can customize this high-level summary in your review settings.

@InfinityBowman InfinityBowman linked an issue Dec 25, 2025 that may be closed by this pull request
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
corates 1d7967f Commit Preview URL Dec 25 2025, 05:35 PM

@coderabbitai

coderabbitai Bot commented Dec 25, 2025

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This PR integrates Stripe payment processing into the application by adding client-side checkout UI components, server-side payment intent creation, and webhook-based subscription management. Updates include new routing, validation schemas, and a Stripe library helper for managing payments.

Changes

Cohort / File(s) Summary
Configuration & Dependency Setup
.cursor/mcp.json, packages/web/package.json
Added Stripe MCP server configuration and @stripe/stripe-js dependency (v8.6.0) for client-side payment processing.
Routing Updates
packages/web/src/Routes.jsx, packages/web/src/components/profile-ui/SettingsPage.jsx
Consolidated billing routes under /billing namespace; added new /billing/checkout nested route and updated navigation links from /settings/billing.
Frontend Billing Components
packages/web/src/components/billing/BillingPage.jsx, packages/web/src/components/billing/CheckoutPage.jsx, packages/web/src/components/billing/PricingTable.jsx, packages/web/src/components/billing/index.js
Added CheckoutPage component with plan selection UI; refactored PricingTable to navigate client-side instead of server redirects; BillingPage now verifies payment status after redirect.
Checkout Form Component
packages/web/src/components/billing/checkout/CheckoutForm.jsx, packages/web/src/components/billing/checkout/index.js
New Stripe Payment Element-based form component handling payment submission, error handling, and redirect on success; includes barrel export.
Frontend API & Stripe Helper
packages/web/src/api/billing.js, packages/web/src/lib/stripe.js
Added createPaymentIntent() API function and getStripe() helper for lazy-loading Stripe.js with publishable key validation.
Backend Validation & Configuration
packages/workers/src/config/validation.js
Introduced billingSchemas with payment intent and checkout validators for tier (pro/team/enterprise) and interval (monthly/yearly).
Backend Payment & Webhook Routes
packages/workers/src/routes/billing/index.js, packages/workers/src/routes/billing/payment-intent.js, packages/workers/src/routes/billing/checkout.js, packages/workers/src/routes/billing/webhooks.js
Added POST /api/billing/payment-intent endpoint; implemented createPaymentIntent() to build Stripe subscriptions with payment intents; enhanced webhook handling to create/update DB subscription records on status changes (including incomplete\_expired tracking).

Sequence Diagram

sequenceDiagram
    participant User as User
    participant Web as Checkout Page
    participant StripeJS as Stripe.js
    participant API as Backend API
    participant Stripe as Stripe Service
    participant DB as Database
    participant Webhook as Webhook Handler

    User->>Web: Select tier & navigate to /billing/checkout
    Web->>API: POST /api/billing/payment-intent (tier, interval)
    API->>Stripe: Create Subscription (default_incomplete)
    Stripe-->>API: Return subscription with payment_intent
    API-->>Web: Return clientSecret
    
    Web->>StripeJS: Initialize Payment Element
    StripeJS->>Web: Payment form ready
    User->>Web: Enter payment details & submit
    Web->>StripeJS: confirmPayment(clientSecret)
    StripeJS->>Stripe: Process payment
    Stripe-->>StripeJS: Payment succeeded/failed
    
    alt Payment Succeeded
        StripeJS-->>Web: Redirect to /billing
        Webhook->>Stripe: Fetch subscription status
        Webhook->>DB: Create/Update subscription record
        DB-->>Webhook: Confirmed
        Web->>API: Verify payment & refetch subscription
        API-->>Web: Active subscription
    else Payment Failed
        StripeJS-->>Web: Show error message
    end
    
    Note over Webhook,DB: webhook.stripe.com triggers<br/>on subscription.updated
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 Hop, skip, and a Stripe!
Payments now flow with delight,
From checkout to webhook's relay,
Subscriptions bloom bright,
A billing dance done just right! 💳✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.67% 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 '45 set up stripe' directly reflects the main objective of the pull request, which is to set up Stripe integration. It references the GitHub issue number (45) and clearly describes the primary change.
✨ 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 45-set-up-stripe

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (7)
packages/web/src/api/billing.js (1)

119-132: Consider using handleFetchError for consistent error handling.

Per coding guidelines, frontend fetch calls should use handleFetchError from @/lib/error-utils.js. However, I notice the existing functions in this file use the same manual pattern. For consistency within this file, this is acceptable, but consider a follow-up refactor to align all billing API functions with the guideline.

Also, the JSDoc return type shows { clientSecret: string } but the server returns both clientSecret and subscriptionId. Consider updating the type annotation:

 /**
  * Create a payment intent for Stripe Elements
  * @param {string} tier - Subscription tier
  * @param {'monthly' | 'yearly'} interval - Billing interval
- * @returns {Promise<{ clientSecret: string }>}
+ * @returns {Promise<{ clientSecret: string, subscriptionId: string }>}
  */
packages/workers/src/routes/billing/webhooks.js (2)

89-89: Remove unused stripe parameter.

The stripe parameter is passed to handleSubscriptionUpdated but is never used within the function.

Proposed fix
     case 'customer.subscription.created':
     case 'customer.subscription.updated':
-      await handleSubscriptionUpdated(db, stripe, event.data.object);
+      await handleSubscriptionUpdated(db, event.data.object);
       break;

And update the function signature:

-async function handleSubscriptionUpdated(db, stripe, subscription) {
+async function handleSubscriptionUpdated(db, subscription) {

216-230: Simplify duplicate logging for incomplete_expired status.

The incomplete_expired status is logged twice with the same message - once when !updated (lines 216-221) and again in the else-if branch (lines 224-227). This creates redundant code paths.

Proposed fix
   const updated = await updateSubscriptionByStripeId(db, subscription.id, updates);

-  // If subscription doesn't exist in DB and status is incomplete_expired, log but don't create
-  // These are abandoned checkouts that Stripe automatically cleaned up
-  if (!updated && subscription.status === 'incomplete_expired') {
-    console.log(
-      `Incomplete subscription expired (abandoned checkout): ${subscription.id} - Stripe automatically cleaned up`,
-    );
-    return;
-  }
-
-  // Log incomplete_expired subscriptions for monitoring abandoned checkouts
+  // Log subscription update or abandoned checkout
   if (subscription.status === 'incomplete_expired') {
     console.log(
-      `Incomplete subscription expired (abandoned checkout): ${subscription.id} - Stripe automatically cleaned up`,
+      `Incomplete subscription expired (abandoned checkout): ${subscription.id} - ${updated ? 'DB updated' : 'no DB record to update'}`,
     );
-  } else {
+  } else if (updated) {
     console.log(`Subscription updated: ${subscription.id} - ${tier} (${subscription.status})`);
   }
packages/web/src/components/billing/BillingPage.jsx (2)

20-20: Unused verifyingPayment signal.

The verifyingPayment signal is created and updated in the onMount handler, but it's never used in the UI. Consider either using it to show a verification loading state (e.g., a spinner or message) while the payment is being verified, or remove it if unnecessary.

Example usage in UI
<Show when={verifyingPayment()}>
  <div class='mb-6 flex items-center rounded-lg border border-blue-200 bg-blue-50 p-4'>
    <span>Verifying payment...</span>
  </div>
</Show>

27-51: Payment verification flow looks good.

The logic correctly handles both Stripe Elements flow (with payment_intent_client_secret) and legacy Checkout Session redirects. A few observations:

  1. Error is logged but the user isn't notified - consider using handleError with showToast for user feedback.
  2. The flow appropriately guards against null Stripe instance.
Optional: Add user-visible error handling
       } catch (error) {
-        console.error('Error verifying payment intent:', error);
+        console.error('Error verifying payment intent:', error);
+        const { handleError } = await import('@/lib/error-utils.js');
+        await handleError(error, { toastTitle: 'Verification Error' });
       } finally {
packages/web/src/components/billing/CheckoutPage.jsx (1)

111-118: Use <For> component for list rendering.

Per SolidJS coding guidelines, use the For component instead of .map() for rendering lists. This provides better performance and proper keying.

Proposed fix
+import { createResource, Show, For } from 'solid-js';
...
-                      {currentPlan()
-                        ?.features.slice(0, 5)
-                        .map(feature => (
-                          <li class='flex items-start'>
-                            <span class='mr-2 text-green-500'>•</span>
-                            <span>{feature}</span>
-                          </li>
-                        ))}
+                      <For each={currentPlan()?.features.slice(0, 5)}>
+                        {feature => (
+                          <li class='flex items-start'>
+                            <span class='mr-2 text-green-500'>•</span>
+                            <span>{feature}</span>
+                          </li>
+                        )}
+                      </For>
packages/workers/src/config/validation.js (1)

136-161: Consider DRY refactor for duplicate schemas.

The paymentIntent and checkout schemas are identical. Consider extracting the common shape to reduce duplication. This also makes future changes easier to maintain.

Proposed refactor
 /**
  * Billing schemas
  */
+const billingTierIntervalSchema = z.object({
+  tier: z.enum(['pro', 'team', 'enterprise'], {
+    error: "Tier must be one of: 'pro', 'team', 'enterprise'",
+  }),
+  interval: z
+    .enum(['monthly', 'yearly'], {
+      error: "Interval must be 'monthly' or 'yearly'",
+    })
+    .default('monthly'),
+});
+
 export const billingSchemas = {
-  paymentIntent: z.object({
-    tier: z.enum(['pro', 'team', 'enterprise'], {
-      error: "Tier must be one of: 'pro', 'team', 'enterprise'",
-    }),
-    interval: z
-      .enum(['monthly', 'yearly'], {
-        error: "Interval must be 'monthly' or 'yearly'",
-      })
-      .default('monthly'),
-  }),
-
-  checkout: z.object({
-    tier: z.enum(['pro', 'team', 'enterprise'], {
-      error: "Tier must be one of: 'pro', 'team', 'enterprise'",
-    }),
-    interval: z
-      .enum(['monthly', 'yearly'], {
-        error: "Interval must be 'monthly' or 'yearly'",
-      })
-      .default('monthly'),
-  }),
+  paymentIntent: billingTierIntervalSchema,
+  checkout: billingTierIntervalSchema,
 };

Otherwise, the schemas correctly follow the Zod v4 patterns established in this file. Based on learnings, validation schemas should be added to this file and reuse common fields when possible.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fd21fef and 1d7967f.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (17)
  • .cursor/mcp.json
  • packages/web/package.json
  • packages/web/src/Routes.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/components/billing/index.js
  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/lib/stripe.js
  • packages/workers/src/config/validation.js
  • packages/workers/src/routes/billing/checkout.js
  • packages/workers/src/routes/billing/index.js
  • packages/workers/src/routes/billing/payment-intent.js
  • packages/workers/src/routes/billing/webhooks.js
🧰 Additional context used
📓 Path-based instructions (20)
**/*

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Do not use emojis in code, comments, documentation, or commit messages

NEVER use emojis anywhere in code, comments, documentation, plan files, or commit messages. This includes unicode symbols. For UI icons, use solid-icons library or SVGs only.

Files:

  • packages/workers/src/routes/billing/checkout.js
  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/lib/stripe.js
  • packages/workers/src/config/validation.js
  • packages/workers/src/routes/billing/payment-intent.js
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/Routes.jsx
  • packages/workers/src/routes/billing/index.js
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/package.json
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/workers/src/routes/billing/webhooks.js
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,jsx,ts,tsx}: Prefer modern ES6+ syntax and features
Use aliases for imports when appropriate to improve readability

**/*.{js,jsx,ts,tsx}: Prefer modern ES6+ syntax and features in JavaScript/TypeScript code
Comments should explain why something is being done, not narrate what the code does. Avoid comments that repeat variable names or describe obvious code behavior.

Files:

  • packages/workers/src/routes/billing/checkout.js
  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/lib/stripe.js
  • packages/workers/src/config/validation.js
  • packages/workers/src/routes/billing/payment-intent.js
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/Routes.jsx
  • packages/workers/src/routes/billing/index.js
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/workers/src/routes/billing/webhooks.js
packages/workers/src/**/*.{js,ts}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/workers/src/**/*.{js,ts}: Use Zod for schema and input validation on the backend
Use Drizzle ORM for database interactions and migrations
Use Better-Auth for authentication and user management

packages/workers/src/**/*.{js,ts}: Use Zod for schema and input validation on the backend
Use Better-Auth for authentication and user management

packages/workers/src/**/*.{js,ts}: Always use createDomainError from @corates/shared for error handling in backend routes with predefined error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS)
Wrap database operations in try-catch blocks and return domain errors using createDomainError(SYSTEM_ERRORS.DB_ERROR, {...}) instead of throwing raw errors
Use validation middleware with validateRequest(schema) to handle validation errors automatically rather than manually creating validation errors in routes

Files:

  • packages/workers/src/routes/billing/checkout.js
  • packages/workers/src/config/validation.js
  • packages/workers/src/routes/billing/payment-intent.js
  • packages/workers/src/routes/billing/index.js
  • packages/workers/src/routes/billing/webhooks.js
packages/workers/src/routes/**/*.js

📄 CodeRabbit inference engine (.cursor/rules/api-routes.mdc)

packages/workers/src/routes/**/*.js: Always use validateRequest middleware for request body validation in API routes, passing appropriate schema from validation config
Use validateQueryParams middleware for validating query string parameters with Zod schemas
Always create database client from environment using createDb(c.env.DB) in route handlers
Use db.batch() for related database operations that must be atomic (succeed or fail together)
Always use Drizzle ORM for database queries; never use raw SQL
Always use createDomainError from @corates/shared for error responses, with appropriate error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS)
Never create error objects manually or throw error strings; use error constants from @corates/shared
Order middleware chain in routes as: authentication (requireAuth), authorization (requireEntitlement, requireQuota), validation (validateRequest, validateQueryParams), then route handler
Use getAuth(c) to retrieve authenticated user information in route handlers
Use c.get('validatedBody') to access request body data after validation middleware
Use c.get('validatedQuery') to access query parameters after validation middleware

Files:

  • packages/workers/src/routes/billing/checkout.js
  • packages/workers/src/routes/billing/payment-intent.js
  • packages/workers/src/routes/billing/index.js
  • packages/workers/src/routes/billing/webhooks.js
packages/{web,workers}/src/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/{web,workers}/src/**/*.{js,ts,jsx,tsx}: Never throw string literals; always throw Error objects or return domain errors from API routes
Use error utility functions like isErrorCode from @corates/shared or @/lib/error-utils.js to check specific error types instead of manual string comparisons

Files:

  • packages/workers/src/routes/billing/checkout.js
  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/lib/stripe.js
  • packages/workers/src/config/validation.js
  • packages/workers/src/routes/billing/payment-intent.js
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/Routes.jsx
  • packages/workers/src/routes/billing/index.js
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/BillingPage.jsx
  • packages/workers/src/routes/billing/webhooks.js
packages/workers/src/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/workers.mdc)

ALWAYS use db.batch() for multiple related database operations to ensure atomicity in Drizzle transactions. Single independent operations do not need batch.

Files:

  • packages/workers/src/routes/billing/checkout.js
  • packages/workers/src/config/validation.js
  • packages/workers/src/routes/billing/payment-intent.js
  • packages/workers/src/routes/billing/index.js
  • packages/workers/src/routes/billing/webhooks.js
packages/workers/src/routes/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/workers.mdc)

ALWAYS validate request bodies using validateRequest middleware from validation config. Do not perform manual validation.

Files:

  • packages/workers/src/routes/billing/checkout.js
  • packages/workers/src/routes/billing/payment-intent.js
  • packages/workers/src/routes/billing/index.js
  • packages/workers/src/routes/billing/webhooks.js
packages/web/src/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/web/src/**/*.{js,jsx,ts,tsx}: For UI icons, use the solid-icons library or SVGs only. Do not use emojis
Ensure browser compatibility for all frontend code (Safari is usually problematic)
Keep files small, focused, and modular. If a file exceeds a high number of lines, consider refactoring by extracting sub-modules into a folder with index.jsx and helper components, moving complex logic into separate utility files or primitives, or splitting large forms into section components
Do NOT prop-drill application state. Shared or cross-feature state must live in external stores under packages/web/src/stores/ or relative to the component file
Use createMemo for derived values to ensure they update reactively

Use import aliases from jsconfig.json instead of relative paths

Files:

  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/lib/stripe.js
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/Routes.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/BillingPage.jsx
packages/web/src/components/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

packages/web/src/components/**/*.{js,jsx,ts,tsx}: Use responsive design principles for UI components
Group related components in subdirectories with an index.js barrel export
Use Zag.js for UI components and design system
Zag component exist in packages/web/src/components/zag/* and should be reused. Check the README.md in that folder for a list of existing components before adding new components and when debugging
Components should receive at most 1–5 props, and only for local configuration, not shared state. If a component would need more than 5 props, move the shared data into an external store, a primitive, or Solid context
Do not destructure props in SolidJS components as it breaks reactivity. Instead, access props directly from the props object or wrap them in a function to ensure they are always up-to-date
Components should be lean and focused. They should not implement business logic; move that into stores, utilities, or primitives
Never have a component act as a God component coordinating multiple large concerns

Files:

  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/BillingPage.jsx
packages/{web,ui}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

Group related components in subdirectories with barrel exports

Files:

  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/Routes.jsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/BillingPage.jsx
packages/{web,landing}/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

packages/{web,landing}/src/**/*.{jsx,tsx}: Use Ark UI components from @corates/ui package, not local component implementations
Use solid-icons library (e.g., solid-icons/bi, solid-icons/fi) for icon imports

Files:

  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/Routes.jsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/BillingPage.jsx
packages/web/src/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

packages/web/src/**/*.{jsx,tsx}: In SolidJS, do NOT prop-drill application state. Import stores directly where needed instead.
In SolidJS, do NOT destructure props. Access props.field directly or wrap in a function: () => props.field
In SolidJS components, components should receive at most 1-5 props (local config only, not shared state)

Files:

  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/Routes.jsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/BillingPage.jsx
packages/web/src/**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)

In SolidJS, use createMemo for derived values

Files:

  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/lib/stripe.js
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/Routes.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/BillingPage.jsx
packages/web/src/**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)

packages/web/src/**/*.{js,ts,jsx,tsx}: Always use handleFetchError from @/lib/error-utils.js for fetch calls in frontend code with options like { showToast: true } for error handling
Use createFormErrorSignals from @/lib/form-errors.js for form validation error handling with field-level and global error management

Files:

  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/lib/stripe.js
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/Routes.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/BillingPage.jsx
{packages/web/**,packages/landing/**}/**/*.{jsx,tsx,js,ts}

📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)

{packages/web/**,packages/landing/**}/**/*.{jsx,tsx,js,ts}: Never destructure props in SolidJS components - destructuring breaks reactivity. Access props directly (e.g., props.name) or wrap in a function (e.g., const name = () => props.name) to maintain reactivity.
Import stores directly in components rather than prop-drilling store data through component hierarchies.
Use separate read and write patterns for stores: import the store directly for reading data (e.g., projectStore.getProjectList()) and import action stores separately for writing (e.g., projectActionsStore.createProject()).
Use createSignal from solid-js for managing simple reactive values. Prefer derived state with signals or memo over effects when possible.
Use createStore from solid-js/store for managing complex objects and arrays that require granular reactivity, enabling fine-grained updates where only affected parts re-render.
Use createMemo from solid-js for derived values that depend on reactive state, ensuring computed values update only when their dependencies change.
Always clean up effects that create subscriptions or timers using the onCleanup function from solid-js. Use effects sparingly, only when derived values won't work well.
Keep components lean and focused on rendering. Move business logic to stores (for shared state and operations), primitives (for reusable hooks/logic), or utilities (for pure functions).
Use the Show component from solid-js for conditional rendering instead of JavaScript ternary operators or logical AND operators.
Use the For component from solid-js for rendering lists. It provides better performance and keying compared to JavaScript's map function in JSX.
When manipulating children in wrapper components, use the children helper from solid-js to ensure proper reactivity and handling of child elements.

Files:

  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/lib/stripe.js
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/Routes.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/BillingPage.jsx
packages/{web,ui}/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

packages/{web,ui}/**/*.{js,jsx,ts,tsx}: Import UI components from '@corates/ui' package instead of local component files. Do not import Ark UI components from local paths like '@/components/zag/' or 'packages/web/src/components/zag/'
Always use 'solid-icons' library for icons. Never use emoji characters or text as icon replacements. Import from specific icon sets like 'solid-icons/bi', 'solid-icons/fi', 'solid-icons/ai', etc.

Files:

  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/lib/stripe.js
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/Routes.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/BillingPage.jsx
packages/web/**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

Use import aliases from 'packages/web/jsconfig.json' instead of relative paths. Aliases include: '@/' (src/), '@components/' (src/components/), '@auth-ui/' (src/components/auth-ui/), '@checklist-ui/' (src/components/checklist-ui/), '@project-ui/' (src/components/project-ui/), '@routes/' (src/routes/), '@primitives/' (src/primitives/), '@api/' (src/api/), '@config/' (src/config/), and '@lib/' (src/lib/)

Files:

  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/lib/stripe.js
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/Routes.jsx
  • packages/web/src/api/billing.js
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/BillingPage.jsx
packages/{web,ui}/**/*.{jsx,tsx}

📄 CodeRabbit inference engine (.cursor/rules/ui-components.mdc)

Use Tailwind CSS classes for styling components

Files:

  • packages/web/src/components/profile-ui/SettingsPage.jsx
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
  • packages/web/src/Routes.jsx
  • packages/web/src/components/billing/PricingTable.jsx
  • packages/web/src/components/billing/BillingPage.jsx
packages/workers/src/config/validation.js

📄 CodeRabbit inference engine (.cursor/rules/api-routes.mdc)

Add new validation schemas to src/config/validation.js and reuse commonFields when possible

Files:

  • packages/workers/src/config/validation.js
packages/workers/src/config/validation.{js,ts}

📄 CodeRabbit inference engine (.cursor/rules/workers.mdc)

Add new Zod validation schemas to config/validation.js and reuse commonFields when possible. Use validateQueryParams for query parameters.

Files:

  • packages/workers/src/config/validation.js
🧠 Learnings (17)
📚 Learning: 2025-12-19T14:49:49.730Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-19T14:49:49.730Z
Learning: Applies to packages/web/src/components/**/*.{js,jsx,ts,tsx} : Group related components in subdirectories with an index.js barrel export

Applied to files:

  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/checkout/index.js
📚 Learning: 2025-12-24T17:22:48.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-24T17:22:48.927Z
Learning: Applies to packages/{web,ui}/src/**/*.{jsx,tsx} : Group related components in subdirectories with barrel exports

Applied to files:

  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/Routes.jsx
📚 Learning: 2025-12-24T17:23:10.082Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-24T17:23:10.082Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx,js,ts} : Import stores directly in components rather than prop-drilling store data through component hierarchies.

Applied to files:

  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/Routes.jsx
📚 Learning: 2025-12-24T17:22:48.927Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2025-12-24T17:22:48.927Z
Learning: Applies to packages/{web,landing}/src/**/*.{jsx,tsx} : Use Ark UI components from corates/ui package, not local component implementations

Applied to files:

  • packages/web/src/components/billing/index.js
  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/index.js
  • packages/web/src/Routes.jsx
  • packages/web/package.json
📚 Learning: 2025-12-24T17:23:10.082Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-24T17:23:10.082Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx,js,ts} : Use `createStore` from solid-js/store for managing complex objects and arrays that require granular reactivity, enabling fine-grained updates where only affected parts re-render.

Applied to files:

  • packages/web/src/components/billing/CheckoutPage.jsx
  • packages/web/src/components/billing/checkout/CheckoutForm.jsx
📚 Learning: 2025-12-24T17:22:31.782Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-24T17:22:31.782Z
Learning: Applies to packages/workers/src/config/validation.js : Add new validation schemas to `src/config/validation.js` and reuse `commonFields` when possible

Applied to files:

  • packages/workers/src/config/validation.js
📚 Learning: 2025-12-24T17:23:22.147Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers.mdc:0-0
Timestamp: 2025-12-24T17:23:22.147Z
Learning: Applies to packages/workers/src/config/validation.{js,ts} : Add new Zod validation schemas to `config/validation.js` and reuse `commonFields` when possible. Use `validateQueryParams` for query parameters.

Applied to files:

  • packages/workers/src/config/validation.js
📚 Learning: 2025-12-24T17:22:56.372Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-24T17:22:56.372Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Use validation middleware with `validateRequest(schema)` to handle validation errors automatically rather than manually creating validation errors in routes

Applied to files:

  • packages/workers/src/config/validation.js
  • packages/workers/src/routes/billing/index.js
📚 Learning: 2025-12-19T14:49:49.730Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-12-19T14:49:49.730Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Use Zod for schema and input validation on the backend

Applied to files:

  • packages/workers/src/config/validation.js
📚 Learning: 2025-12-24T17:22:31.782Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-24T17:22:31.782Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always use `validateRequest` middleware for request body validation in API routes, passing appropriate schema from validation config

Applied to files:

  • packages/workers/src/config/validation.js
  • packages/workers/src/routes/billing/index.js
📚 Learning: 2025-12-24T17:22:31.782Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-24T17:22:31.782Z
Learning: Applies to packages/workers/src/routes/**/*.js : Use `validateQueryParams` middleware for validating query string parameters with Zod schemas

Applied to files:

  • packages/workers/src/config/validation.js
📚 Learning: 2025-12-24T17:23:17.309Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-12-24T17:23:17.309Z
Learning: Applies to packages/web/**/*.{js,jsx,ts,tsx} : Use import aliases from 'packages/web/jsconfig.json' instead of relative paths. Aliases include: '@/*' (src/*), 'components/*' (src/components/*), 'auth-ui/*' (src/components/auth-ui/*), 'checklist-ui/*' (src/components/checklist-ui/*), 'project-ui/*' (src/components/project-ui/*), 'routes/*' (src/routes/*), 'primitives/*' (src/primitives/*), 'api/*' (src/api/*), 'config/*' (src/config/*), and 'lib/*' (src/lib/*)

Applied to files:

  • packages/web/src/Routes.jsx
📚 Learning: 2025-12-24T17:23:22.147Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers.mdc:0-0
Timestamp: 2025-12-24T17:23:22.147Z
Learning: Applies to packages/workers/src/routes/**/*.{js,ts,jsx,tsx} : ALWAYS validate request bodies using `validateRequest` middleware from validation config. Do not perform manual validation.

Applied to files:

  • packages/workers/src/routes/billing/index.js
📚 Learning: 2025-12-24T17:22:31.782Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-24T17:22:31.782Z
Learning: Applies to packages/workers/src/routes/**/*.js : Order middleware chain in routes as: authentication (`requireAuth`), authorization (`requireEntitlement`, `requireQuota`), validation (`validateRequest`, `validateQueryParams`), then route handler

Applied to files:

  • packages/workers/src/routes/billing/index.js
📚 Learning: 2025-12-24T17:22:31.782Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2025-12-24T17:22:31.782Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always use `createDomainError` from `corates/shared` for error responses, with appropriate error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS)

Applied to files:

  • packages/workers/src/routes/billing/index.js
  • packages/workers/src/routes/billing/webhooks.js
📚 Learning: 2025-12-24T17:22:56.372Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2025-12-24T17:22:56.372Z
Learning: Applies to packages/workers/src/**/*.{js,ts} : Always use `createDomainError` from `corates/shared` for error handling in backend routes with predefined error constants (PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS)

Applied to files:

  • packages/workers/src/routes/billing/index.js
📚 Learning: 2025-12-24T17:23:17.309Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/ui-components.mdc:0-0
Timestamp: 2025-12-24T17:23:17.309Z
Learning: Applies to packages/{web,ui}/**/*.{js,jsx,ts,tsx} : Import UI components from 'corates/ui' package instead of local component files. Do not import Ark UI components from local paths like '@/components/zag/' or 'packages/web/src/components/zag/'

Applied to files:

  • packages/web/package.json
🧬 Code graph analysis (9)
packages/web/src/components/billing/CheckoutPage.jsx (3)
packages/web/src/components/billing/BillingPage.jsx (1)
  • searchParams (16-16)
packages/web/src/api/billing.js (1)
  • getPlans (40-52)
packages/web/src/components/billing/checkout/CheckoutForm.jsx (3)
  • tier (22-22)
  • interval (23-23)
  • CheckoutForm (13-176)
packages/web/src/components/billing/checkout/CheckoutForm.jsx (2)
packages/web/src/api/billing.js (6)
  • error (29-29)
  • error (47-47)
  • error (68-70)
  • error (88-88)
  • error (127-127)
  • createPaymentIntent (119-132)
packages/web/src/lib/stripe.js (1)
  • getStripe (16-27)
packages/workers/src/routes/billing/payment-intent.js (3)
packages/workers/src/db/client.js (1)
  • createDb (9-11)
packages/workers/src/config/stripe.js (1)
  • getPriceId (48-50)
packages/workers/src/db/subscriptions.js (1)
  • getSubscriptionByUserId (15-23)
packages/web/src/Routes.jsx (1)
packages/web/src/components/billing/CheckoutPage.jsx (1)
  • CheckoutPage (12-129)
packages/workers/src/routes/billing/index.js (2)
packages/workers/src/config/validation.js (4)
  • validateRequest (261-287)
  • billingSchemas (139-161)
  • billingSchemas (139-161)
  • result (205-205)
packages/workers/src/routes/billing/payment-intent.js (1)
  • createPaymentIntent (19-105)
packages/web/src/api/billing.js (1)
packages/workers/src/routes/billing/payment-intent.js (3)
  • createPaymentIntent (19-105)
  • error (26-29)
  • error (94-97)
packages/web/src/components/billing/PricingTable.jsx (2)
packages/web/src/components/billing/CheckoutPage.jsx (2)
  • plans (14-14)
  • tier (16-16)
packages/web/src/api/billing.js (1)
  • getPlans (40-52)
packages/web/src/components/billing/BillingPage.jsx (2)
packages/web/src/components/billing/checkout/CheckoutForm.jsx (2)
  • clientSecret (20-20)
  • stripe (17-17)
packages/web/src/lib/stripe.js (1)
  • getStripe (16-27)
packages/workers/src/routes/billing/webhooks.js (1)
packages/workers/src/db/subscriptions.js (4)
  • existing (77-77)
  • getSubscriptionByStripeSubscriptionId (47-55)
  • upsertSubscription (63-118)
  • updateSubscriptionByStripeId (127-139)
⏰ 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). (2)
  • GitHub Check: Workers Builds: corates
  • GitHub Check: Workers Builds: corates-workers-prod
🔇 Additional comments (23)
packages/workers/src/routes/billing/checkout.js (1)

47-48: LGTM! URL paths updated to align with new billing route structure.

The success and cancel URLs are correctly updated to use the /billing namespace, consistent with the routing changes in this PR.

packages/web/src/components/profile-ui/SettingsPage.jsx (1)

84-86: LGTM! Billing link updated to match new route structure.

The navigation link correctly points to the new /billing path.

packages/workers/src/routes/billing/payment-intent.js (1)

50-72: Well-documented subscription creation flow.

The comments clearly explain the default_incomplete payment behavior and Stripe's automatic cleanup of incomplete subscriptions after 23 hours. The metadata inclusion for webhook handling is appropriate.

packages/web/src/components/billing/PricingTable.jsx (2)

18-22: LGTM! Clean simplification of the checkout flow.

The refactor from async redirect to client-side navigation simplifies the component by removing loading state management. The enterprise tier guard and early return pattern are appropriate.


31-33: LGTM! Button disabled state correctly updated.

The disabled logic now properly includes the enterprise tier, aligning with the guard in handleUpgrade.

packages/workers/src/routes/billing/webhooks.js (1)

158-188: Good handling of Elements flow subscription creation.

The logic correctly handles the case where subscriptions are created in Stripe before payment is confirmed (Elements flow). Creating the DB record only when the subscription becomes active is the right approach.

packages/web/package.json (1)

23-23: LGTM! Stripe.js dependency added correctly.

The @stripe/stripe-js package is the official client-side SDK for Stripe integration. Version 8.6.0 is the latest release (published December 15, 2025), so this dependency is current.

packages/web/src/components/billing/checkout/index.js (1)

1-2: LGTM!

Barrel export follows the project convention for grouping related components with index.js exports. Based on learnings, this aligns with the guideline to group related components in subdirectories with barrel exports.

packages/web/src/components/billing/index.js (1)

6-7: LGTM!

The new exports follow the established pattern and maintain consistency with the existing billing component exports.

packages/web/src/Routes.jsx (2)

17-17: LGTM!

Import uses the correct alias per project conventions.


44-45: LGTM!

Billing routes are correctly placed under ProtectedGuard, ensuring authentication is required for billing operations. The route hierarchy is clean with /billing as the main page and /billing/checkout for the checkout flow.

packages/web/src/components/billing/CheckoutPage.jsx (3)

6-10: LGTM!

Imports correctly use project aliases and follow the established patterns.


16-38: LGTM!

Good use of derived accessors for reactive values. The computed helpers (currentPlan, planName, planPrice, formatPrice) correctly handle null cases and edge conditions.


131-147: LGTM!

Skeleton components are simple and appropriately scoped to this file.

packages/web/src/lib/stripe.js (1)

16-27: LGTM!

Good lazy initialization pattern for Stripe.js. The singleton approach prevents redundant network requests, and the null check with warning helps debug missing configuration.

packages/web/src/components/billing/checkout/CheckoutForm.jsx (5)

22-23: LGTM!

Correct pattern for prop access in SolidJS - using accessor functions instead of destructuring maintains reactivity.


26-72: LGTM!

Stripe initialization is well-structured:

  • Proper error handling with user-visible error state
  • Loading state managed correctly
  • PaymentElement created and mounted after client secret is obtained
  • Customized appearance matches the app's design system

74-79: LGTM!

Good cleanup practice - unmounting the PaymentElement prevents memory leaks and orphaned DOM elements.


81-116: LGTM!

Payment submission handles both success and error cases properly:

  • Guards against uninitialized state before submission
  • Uses redirect: 'if_required' which correctly handles 3DS/SCA flows
  • Falls back to client-side navigation when no redirect is needed
  • Error handling uses the project's handleError utility as per coding guidelines

118-175: LGTM!

Clean form UI with appropriate accessibility considerations:

  • Disabled state properly tied to loading and initialization status
  • Error display uses accessible color contrast
  • Loading spinner provides visual feedback
  • Security notice builds user trust
packages/workers/src/routes/billing/index.js (3)

13-13: LGTM!

The new imports are necessary for the validation middleware and payment intent functionality, and all are properly used throughout the file.

Also applies to: 16-16


122-124: LGTM!

The checkout route now correctly uses validation middleware following the proper middleware chain order (auth → validation → handler). Using c.get('validatedBody') after the validation middleware is the correct pattern and eliminates manual validation logic.

As per coding guidelines and learnings, this approach ensures validation errors are handled automatically by the middleware.


143-171: LGTM!

The new payment-intent route is well-implemented and follows all established patterns:

  • Correct middleware chain order (auth → validation → handler)
  • Proper use of validated request body
  • Consistent error handling with domain errors
  • Matches the structure and style of existing routes

As per coding guidelines, the route properly uses validation middleware and domain error handling.

Comment thread .cursor/mcp.json
Comment on lines +9 to +11
"stripe": {
"url": "https://mcp.stripe.com"
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cat -n .cursor/mcp.json

Repository: InfinityBowman/corates

Length of output: 532


🌐 Web query:

Stripe MCP server configuration format requirements

💡 Result:

Short summary — accepted config formats & required fields

  • MCP client config (JSON) — register a Stripe MCP server under "mcpServers" with one of:

    • remote/HTTP transport: { "mcpServers": { "stripe": { "transport":"streamableHttp", "url":"" } } }.
    • local/exec invocation: { "mcpServers": { "stripe": { "command":"npx", "args":[...], "env":{...} } } }.
      Required pieces: a way to start/connect the server (command+args or url/transport) and credentials passed either via env vars or forwarded variables. [1][5]
  • Environment / credentials:

    • Stripe secret key must be provided (e.g., STRIPE_SECRET_KEY or --api-key argument). Avoid embedding long-lived keys in plain forwarded vars when possible. You can forward env vars to hosted servers using FORWARD_VAR_. [2][4][6]
  • Server-specific config object (optional): when running Stripe’s Agent Toolkit locally, the toolkit accepts a configuration object (JSON) that toggles tools/actions (e.g., actions.paymentLinks.create = true) and other runtime options. [3][2]

  • Practical examples:

    • npx run: npx -y @stripe/mcp --tools=all --api-key=YOUR_STRIPE_SECRET_KEY (or set STRIPE_SECRET_KEY in env). [3][2]
    • client config JSON example (local exec + env): { "mcpServers": { "stripe": { "command":"npx", "args":["-y","@stripe/mcp","--tools=customers.create"], "env": { "STRIPE_SECRET_KEY":"" } } } }. [4][5]

If you want, tell me which MCP client (Open-MCP, Claude, MintMCP, etc.) you’re using and I’ll produce a ready-to-use config file for it.


Fix incomplete Stripe MCP server configuration.

The stripe entry is missing two required fields. According to Stripe MCP documentation, HTTP-based MCP configurations must include:

  • "transport":"streamableHttp" to specify the HTTP transport protocol
  • STRIPE_SECRET_KEY credential in the env object

Correct configuration should be:

"stripe": {
  "transport": "streamableHttp",
  "url": "https://mcp.stripe.com",
  "env": {
    "STRIPE_SECRET_KEY": "<your-stripe-secret-key>"
  }
}
🤖 Prompt for AI Agents
In .cursor/mcp.json around lines 9 to 11, the Stripe MCP entry is incomplete:
add the required "transport":"streamableHttp" field and an env object containing
the STRIPE_SECRET_KEY credential; update the JSON so the stripe object includes
transport set to "streamableHttp", the existing url, and an env block with
STRIPE_SECRET_KEY set to the appropriate secret value (or a placeholder) to
satisfy Stripe MCP HTTP configuration requirements.

Comment on lines +33 to +35
// Check for existing subscription
const existingSubscription = await getSubscriptionByUserId(db, user.id);
const customerId = existingSubscription?.stripeCustomerId;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n packages/workers/src/routes/billing/payment-intent.js

Repository: InfinityBowman/corates

Length of output: 4161


🏁 Script executed:

cat -n packages/workers/src/routes/billing/index.js

Repository: InfinityBowman/corates

Length of output: 8695


🏁 Script executed:

cat -n packages/workers/src/db/subscriptions.js | head -100

Repository: InfinityBowman/corates

Length of output: 3310


🏁 Script executed:

cat -n packages/workers/src/db/schema.js | grep -A 30 "subscriptions\|export const subscriptions"

Repository: InfinityBowman/corates

Length of output: 1806


🏁 Script executed:

# Check if there's any validation on the frontend for preventing active subscribers from creating payment intents
find packages/web -type f \( -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" \) -exec rg -l "payment-intent|createPaymentIntent" {} \;

Repository: InfinityBowman/corates

Length of output: 159


🏁 Script executed:

cat -n packages/web/src/api/billing.js

Repository: InfinityBowman/corates

Length of output: 4507


🏁 Script executed:

cat -n packages/web/src/components/billing/checkout/CheckoutForm.jsx

Repository: InfinityBowman/corates

Length of output: 6684


🏁 Script executed:

cat -n packages/workers/src/config/validation.js | grep -A 20 "billingSchemas\|paymentIntent"

Repository: InfinityBowman/corates

Length of output: 853


🏁 Script executed:

cat -n packages/workers/src/routes/billing/webhooks.js | head -100

Repository: InfinityBowman/corates

Length of output: 3503


🏁 Script executed:

cat -n packages/workers/src/routes/billing/webhooks.js | sed -n '100,200p'

Repository: InfinityBowman/corates

Length of output: 4226


Add validation to prevent users with existing active or incomplete subscriptions from creating new payment intents.

Currently, users can create multiple payment intents and Stripe subscriptions even if they already have an active or incomplete subscription. While the database record gets overwritten via upsert, Stripe itself accumulates multiple subscriptions for the same customer, with identical userId metadata. Add a check after line 33 to reject requests from users with subscriptions that have status other than 'canceled' or 'incomplete_expired':

// Check for existing subscription
const existingSubscription = await getSubscriptionByUserId(db, user.id);
const customerId = existingSubscription?.stripeCustomerId;

// Prevent creating payment intent if user already has an active or incomplete subscription
if (existingSubscription && !['canceled', 'incomplete_expired'].includes(existingSubscription.status)) {
  const error = createDomainError(VALIDATION_ERRORS.INVALID_INPUT, {
    context: 'existing_subscription',
    message: 'User already has an active subscription',
  });
  throw error;
}
🤖 Prompt for AI Agents
In packages/workers/src/routes/billing/payment-intent.js around lines 33 to 35,
add a validation after obtaining existingSubscription to prevent creating
payment intents when a user already has a subscription whose status is not
'canceled' or 'incomplete_expired'; if existingSubscription exists and its
status is not in that allowed list, create and throw a domain validation error
(use createDomainError with VALIDATION_ERRORS.INVALID_INPUT, include context
'existing_subscription' and a clear message like 'User already has an active
subscription') so the request is rejected before creating a new payment intent
or Stripe subscription.

Comment on lines +38 to +48
let customer;
if (customerId) {
customer = await stripe.customers.retrieve(customerId);
} else {
customer = await stripe.customers.create({
email: user.email,
metadata: {
userId: user.id,
},
});
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Handle potential deleted customer scenario.

If a customer exists in the local database but was deleted in Stripe, stripe.customers.retrieve(customerId) will throw an error. Consider wrapping this in a try-catch or checking if the customer is deleted.

Proposed fix
   let customer;
   if (customerId) {
-    customer = await stripe.customers.retrieve(customerId);
+    try {
+      customer = await stripe.customers.retrieve(customerId);
+      if (customer.deleted) {
+        customer = await stripe.customers.create({
+          email: user.email,
+          metadata: { userId: user.id },
+        });
+      }
+    } catch {
+      customer = await stripe.customers.create({
+        email: user.email,
+        metadata: { userId: user.id },
+      });
+    }
   } else {
     customer = await stripe.customers.create({
       email: user.email,
       metadata: {
         userId: user.id,
       },
     });
   }
🤖 Prompt for AI Agents
In packages/workers/src/routes/billing/payment-intent.js around lines 38 to 48,
the call to stripe.customers.retrieve(customerId) can throw if the Stripe
customer was deleted; wrap the retrieve in a try-catch, and on error check for
Stripe's "resource_missing" (or relevant) error indicating a deleted/missing
customer, then create a new Stripe customer with the user's email/metadata,
assign it to customer, and persist the new customer.id back to your local user
record (or clear the stored customerId) before continuing; rethrow non-missing
errors.

@InfinityBowman
InfinityBowman deleted the 45-set-up-stripe branch January 5, 2026 18:39
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.

Set up Stripe

2 participants