45 set up stripe - #152
Conversation
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
corates | 1d7967f | Commit Preview URL | Dec 25 2025, 05:35 PM |
📝 WalkthroughWalkthroughThis 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (7)
packages/web/src/api/billing.js (1)
119-132: Consider usinghandleFetchErrorfor consistent error handling.Per coding guidelines, frontend fetch calls should use
handleFetchErrorfrom@/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 bothclientSecretandsubscriptionId. 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 unusedstripeparameter.The
stripeparameter is passed tohandleSubscriptionUpdatedbut 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 forincomplete_expiredstatus.The
incomplete_expiredstatus 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: UnusedverifyingPaymentsignal.The
verifyingPaymentsignal is created and updated in theonMounthandler, 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:
- Error is logged but the user isn't notified - consider using
handleErrorwithshowToastfor user feedback.- 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
Forcomponent 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
paymentIntentandcheckoutschemas 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (17)
.cursor/mcp.jsonpackages/web/package.jsonpackages/web/src/Routes.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/BillingPage.jsxpackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/components/billing/checkout/index.jspackages/web/src/components/billing/index.jspackages/web/src/components/profile-ui/SettingsPage.jsxpackages/web/src/lib/stripe.jspackages/workers/src/config/validation.jspackages/workers/src/routes/billing/checkout.jspackages/workers/src/routes/billing/index.jspackages/workers/src/routes/billing/payment-intent.jspackages/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.jspackages/web/src/components/profile-ui/SettingsPage.jsxpackages/web/src/components/billing/index.jspackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/lib/stripe.jspackages/workers/src/config/validation.jspackages/workers/src/routes/billing/payment-intent.jspackages/web/src/components/billing/checkout/index.jspackages/web/src/Routes.jsxpackages/workers/src/routes/billing/index.jspackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/web/package.jsonpackages/web/src/components/billing/BillingPage.jsxpackages/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.jspackages/web/src/components/profile-ui/SettingsPage.jsxpackages/web/src/components/billing/index.jspackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/lib/stripe.jspackages/workers/src/config/validation.jspackages/workers/src/routes/billing/payment-intent.jspackages/web/src/components/billing/checkout/index.jspackages/web/src/Routes.jsxpackages/workers/src/routes/billing/index.jspackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/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 usecreateDomainErrorfrom@corates/sharedfor 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 usingcreateDomainError(SYSTEM_ERRORS.DB_ERROR, {...})instead of throwing raw errors
Use validation middleware withvalidateRequest(schema)to handle validation errors automatically rather than manually creating validation errors in routes
Files:
packages/workers/src/routes/billing/checkout.jspackages/workers/src/config/validation.jspackages/workers/src/routes/billing/payment-intent.jspackages/workers/src/routes/billing/index.jspackages/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 usevalidateRequestmiddleware for request body validation in API routes, passing appropriate schema from validation config
UsevalidateQueryParamsmiddleware for validating query string parameters with Zod schemas
Always create database client from environment usingcreateDb(c.env.DB)in route handlers
Usedb.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 usecreateDomainErrorfrom@corates/sharedfor 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
UsegetAuth(c)to retrieve authenticated user information in route handlers
Usec.get('validatedBody')to access request body data after validation middleware
Usec.get('validatedQuery')to access query parameters after validation middleware
Files:
packages/workers/src/routes/billing/checkout.jspackages/workers/src/routes/billing/payment-intent.jspackages/workers/src/routes/billing/index.jspackages/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 likeisErrorCodefrom@corates/sharedor@/lib/error-utils.jsto check specific error types instead of manual string comparisons
Files:
packages/workers/src/routes/billing/checkout.jspackages/web/src/components/profile-ui/SettingsPage.jsxpackages/web/src/components/billing/index.jspackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/lib/stripe.jspackages/workers/src/config/validation.jspackages/workers/src/routes/billing/payment-intent.jspackages/web/src/components/billing/checkout/index.jspackages/web/src/Routes.jsxpackages/workers/src/routes/billing/index.jspackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/web/src/components/billing/BillingPage.jsxpackages/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.jspackages/workers/src/config/validation.jspackages/workers/src/routes/billing/payment-intent.jspackages/workers/src/routes/billing/index.jspackages/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
validateRequestmiddleware from validation config. Do not perform manual validation.
Files:
packages/workers/src/routes/billing/checkout.jspackages/workers/src/routes/billing/payment-intent.jspackages/workers/src/routes/billing/index.jspackages/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 thesolid-iconslibrary 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
UsecreateMemofor derived values to ensure they update reactivelyUse import aliases from jsconfig.json instead of relative paths
Files:
packages/web/src/components/profile-ui/SettingsPage.jsxpackages/web/src/components/billing/index.jspackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/lib/stripe.jspackages/web/src/components/billing/checkout/index.jspackages/web/src/Routes.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/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 inpackages/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.jsxpackages/web/src/components/billing/index.jspackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/components/billing/checkout/index.jspackages/web/src/components/billing/PricingTable.jsxpackages/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.jsxpackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/Routes.jsxpackages/web/src/components/billing/PricingTable.jsxpackages/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.jsxpackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/Routes.jsxpackages/web/src/components/billing/PricingTable.jsxpackages/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.jsxpackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/Routes.jsxpackages/web/src/components/billing/PricingTable.jsxpackages/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.jsxpackages/web/src/components/billing/index.jspackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/lib/stripe.jspackages/web/src/components/billing/checkout/index.jspackages/web/src/Routes.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/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 usehandleFetchErrorfrom@/lib/error-utils.jsfor fetch calls in frontend code with options like{ showToast: true }for error handling
UsecreateFormErrorSignalsfrom@/lib/form-errors.jsfor form validation error handling with field-level and global error management
Files:
packages/web/src/components/profile-ui/SettingsPage.jsxpackages/web/src/components/billing/index.jspackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/lib/stripe.jspackages/web/src/components/billing/checkout/index.jspackages/web/src/Routes.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/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()).
UsecreateSignalfrom solid-js for managing simple reactive values. Prefer derived state with signals or memo over effects when possible.
UsecreateStorefrom solid-js/store for managing complex objects and arrays that require granular reactivity, enabling fine-grained updates where only affected parts re-render.
UsecreateMemofrom 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 theonCleanupfunction 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 theShowcomponent from solid-js for conditional rendering instead of JavaScript ternary operators or logical AND operators.
Use theForcomponent 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 thechildrenhelper from solid-js to ensure proper reactivity and handling of child elements.
Files:
packages/web/src/components/profile-ui/SettingsPage.jsxpackages/web/src/components/billing/index.jspackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/lib/stripe.jspackages/web/src/components/billing/checkout/index.jspackages/web/src/Routes.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/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.jsxpackages/web/src/components/billing/index.jspackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/lib/stripe.jspackages/web/src/components/billing/checkout/index.jspackages/web/src/Routes.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/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.jsxpackages/web/src/components/billing/index.jspackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/lib/stripe.jspackages/web/src/components/billing/checkout/index.jspackages/web/src/Routes.jsxpackages/web/src/api/billing.jspackages/web/src/components/billing/PricingTable.jsxpackages/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.jsxpackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/CheckoutForm.jsxpackages/web/src/Routes.jsxpackages/web/src/components/billing/PricingTable.jsxpackages/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.jsand reusecommonFieldswhen 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.jsand reusecommonFieldswhen possible. UsevalidateQueryParamsfor 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.jspackages/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.jspackages/web/src/components/billing/checkout/index.jspackages/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.jspackages/web/src/components/billing/checkout/index.jspackages/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.jspackages/web/src/components/billing/CheckoutPage.jsxpackages/web/src/components/billing/checkout/index.jspackages/web/src/Routes.jsxpackages/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.jsxpackages/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.jspackages/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.jspackages/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.jspackages/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
/billingnamespace, 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
/billingpath.packages/workers/src/routes/billing/payment-intent.js (1)
50-72: Well-documented subscription creation flow.The comments clearly explain the
default_incompletepayment 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
activeis the right approach.packages/web/package.json (1)
23-23: LGTM! Stripe.js dependency added correctly.The
@stripe/stripe-jspackage 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/billingas the main page and/billing/checkoutfor 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
handleErrorutility 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.
| "stripe": { | ||
| "url": "https://mcp.stripe.com" | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n .cursor/mcp.jsonRepository: 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 protocolSTRIPE_SECRET_KEYcredential in theenvobject
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.
| // Check for existing subscription | ||
| const existingSubscription = await getSubscriptionByUserId(db, user.id); | ||
| const customerId = existingSubscription?.stripeCustomerId; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n packages/workers/src/routes/billing/payment-intent.jsRepository: InfinityBowman/corates
Length of output: 4161
🏁 Script executed:
cat -n packages/workers/src/routes/billing/index.jsRepository: InfinityBowman/corates
Length of output: 8695
🏁 Script executed:
cat -n packages/workers/src/db/subscriptions.js | head -100Repository: 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.jsRepository: InfinityBowman/corates
Length of output: 4507
🏁 Script executed:
cat -n packages/web/src/components/billing/checkout/CheckoutForm.jsxRepository: 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 -100Repository: 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.
| let customer; | ||
| if (customerId) { | ||
| customer = await stripe.customers.retrieve(customerId); | ||
| } else { | ||
| customer = await stripe.customers.create({ | ||
| email: user.email, | ||
| metadata: { | ||
| userId: user.id, | ||
| }, | ||
| }); | ||
| } |
There was a problem hiding this comment.
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.
Summary by CodeRabbit
New Features
Chores
✏️ Tip: You can customize this high-level summary in your review settings.