319 payment redirect from landing#320
Conversation
📝 WalkthroughWalkthroughCapture and persist signup plan/interval params, centralize pending-plan redirect handling (trial/checkout/single-project), extend billing API signatures with options, add worker-side pre-check preventing re-subscribe to same tier, integrate OAuth error handling, and remove several large mock UI components. Changes
sequenceDiagram
participant User
participant SignUp as SignUp Component
participant Auth as Auth System
participant Settings as PlansSettings
participant Utils as plan-redirect-utils
participant API as Billing API
participant Worker as Checkout Worker
User->>SignUp: GET /signup?plan=PLAN&interval=INT
SignUp->>Utils: capturePlanParams(urlParams)
Utils->>Utils: validate & store pending plan
User->>Auth: complete auth/profile
Auth->>Utils: hasPendingPlan()
Utils-->>Auth: true
Auth->>Settings: redirect /settings/plans
Settings->>Utils: handlePendingPlanRedirect({ navigate, refetch })
Utils->>Utils: getPendingPlan()
alt trial
Utils->>API: startTrial(options)
API-->>Utils: success/failure
else single_project
Utils->>API: redirectToSingleProjectCheckout(options)
API-->>Utils: redirect
else subscription
Utils->>API: redirectToCheckout(tier, interval, options)
API->>Worker: POST /checkout (tier, interval)
Worker->>Worker: resolveOrgAccess()
alt already on same tier
Worker-->>API: error INVALID_INPUT (already_on_plan)
API-->>Utils: error
else proceed
Worker-->>API: session/redirect
API-->>Utils: session/redirect
end
end
Utils->>Utils: clearPendingPlan()
Utils-->>Settings: { handled, error }
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 📜 Recent review detailsConfiguration used: Organization UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
🧰 Additional context used📓 Path-based instructions (1)**/*📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Files:
⏰ 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). (1)
🔇 Additional comments (1)
✏️ Tip: You can disable this entire section by setting Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
corates | 952d4a0 | Commit Preview URL | Jan 20 2026, 05:31 AM |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In `@packages/web/src/components/settings/pages/PlansSettings.jsx`:
- Around line 115-123: handleDismissError currently sets the page state to
'ready' but leaves the pending plan in storage so the failed redirect can
re-trigger later; update handleDismissError to also clear the pending plan
(e.g., call an existing clearPendingPlan/removePendingPlan helper or implement
one that removes the pending plan key from storage) so that when the user
dismisses the error the pending plan is removed and processPendingPlan won't run
on subsequent visits; reference handleDismissError and processPendingPlan and
add the clear call alongside setPageState('ready').
In `@packages/workers/src/routes/billing/checkout.ts`:
- Around line 279-291: The current guard uses resolveOrgAccess and blocks
checkout whenever currentBilling.effectivePlanId === tier, which wrongly
prevents interval-only changes and upgrades from grant/trial to paid; update the
check so it only short-circuits when the org has an active paid subscription
with the same plan AND the billing interval also matches. Specifically, in the
checkout flow around resolveOrgAccess/currentBilling, refine the condition to
verify the subscription is active (e.g., status or isActive flag on
currentBilling) and compare both currentBilling.effectivePlanId === tier AND
currentBilling.interval === requestedInterval (or similar request param) before
returning the "already_on_plan" error, but allow proceed when subscription is a
grant/trial or when the interval differs. Ensure you reference currentBilling,
effectivePlanId, tier, and the interval/requestedInterval variable in your
condition.
🧹 Nitpick comments (2)
packages/web/src/components/mocks/MockIndex.jsx (1)
115-119: Consider removing the empty "Other Mocks" section.The section header and empty grid div remain after the mock cards were removed. If there are no "Other Mocks" to display, consider removing this section entirely to avoid dead UI code.
Suggested cleanup
</A> </div> </div> - - {/* Other Mocks */} - <div> - <h2 class='mb-3 text-lg font-semibold text-gray-700'>Other Mocks</h2> - <div class='grid gap-4'></div> - </div> </div> </div> );packages/web/src/lib/plan-redirect-utils.js (1)
189-198: Potential race condition:clearPendingPlan()may not execute before page unloads.For checkout redirects,
clearPendingPlan()is called afterredirectToSingleProjectCheckout()/redirectToCheckout(), which setwindow.location.href. While localStorage writes are synchronous and typically fast, there's a race with the page unload. If the clear doesn't complete, the user could be redirected again on return.Consider clearing before the redirect:
Suggested fix
if (pendingPlan === 'single_project') { + clearPendingPlan(); await redirectToSingleProjectCheckout(); - clearPendingPlan(); return { handled: true, error: null }; } // Subscription plans (starter_team, team, unlimited_team) + clearPendingPlan(); await redirectToCheckout(pendingPlan, pendingInterval); - clearPendingPlan(); return { handled: true, error: null };
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (17)
packages/landing/src/lib/config.jspackages/landing/src/routes/pricing.jsxpackages/shared/src/plans/catalog.tspackages/shared/src/plans/index.tspackages/web/src/api/billing.jspackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/SignUp.jsxpackages/web/src/components/mocks/MockIndex.jsxpackages/web/src/components/mocks/MockRoutes.jsxpackages/web/src/components/mocks/ProjectViewDashboard.jsxpackages/web/src/components/mocks/ProjectViewEditorial.jsxpackages/web/src/components/mocks/ProjectViewKanban.jsxpackages/web/src/components/mocks/ProjectWizardMock.jsxpackages/web/src/components/settings/pages/PlansSettings.jsxpackages/web/src/lib/plan-redirect-utils.jspackages/workers/src/routes/billing/checkout.ts
💤 Files with no reviewable changes (5)
- packages/web/src/components/mocks/ProjectViewEditorial.jsx
- packages/web/src/components/mocks/ProjectWizardMock.jsx
- packages/web/src/components/mocks/ProjectViewKanban.jsx
- packages/web/src/components/mocks/ProjectViewDashboard.jsx
- packages/web/src/components/mocks/MockRoutes.jsx
🧰 Additional context used
📓 Path-based instructions (20)
packages/web/src/**/*.{js,jsx}
📄 CodeRabbit inference engine (.cursor/rules/form-state.mdc)
packages/web/src/**/*.{js,jsx}: Save form state to IndexedDB before initiating OAuth redirects (Google Drive, ORCID) using saveFormState() with form type ('createProject' or 'addStudies') and serializable state only
Only save serializable data to IndexedDB—exclude File objects, ArrayBuffers, functions, and other non-serializable objects from form state persistence
Restore form state after OAuth redirects by checking URL restore params with getRestoreParamsFromUrl(), retrieving saved state with getFormState(), restoring to form, clearing saved state with clearFormState(), and clearing URL params with clearRestoreParamsFromUrl()
For temporary File object storage during OAuth flows (e.g., pending PDFs), use projectStore.setPendingProjectData() instead of IndexedDB, as File objects cannot be serialized
Add restore parameters to the URL after OAuth redirects in the format '?restore=&projectId=' to signal form state restoration on mount
Clear URL restore parameters after form state restoration by calling clearRestoreParamsFromUrl() to prevent stale restoration attempts on subsequent navigation
Form state persistence library functions (saveFormState, getFormState, clearFormState, getRestoreParamsFromUrl, clearRestoreParamsFromUrl) are implemented in packages/web/src/lib/formStatePersistence.js and should be imported from@/lib/formStatePersistence.js
Files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/api/billing.jspackages/web/src/components/mocks/MockIndex.jsxpackages/web/src/lib/plan-redirect-utils.jspackages/web/src/components/settings/pages/PlansSettings.jsx
packages/web/src/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)
packages/web/src/**/*.{js,jsx,ts,tsx}: Always useuploadPdffrom@api/pdf-api.jsfor uploading PDF files. Pass object with projectId, studyId, tag ('primary' or 'supplementary'), and fileName
Validate PDF files on frontend by checking file.type includes 'pdf' and file.size is within MAX_PDF_SIZE limit (full validation is done on backend)
UsecachePdfandgetCachedPdffrom@primitives/pdfCache.jsfor caching PDF data in IndexedDB
Implement PDF caching strategy: check cache first, download from server if not cached, then cache the downloaded PDF
Store only PDF metadata in Yjs (id, name, size, tag, uploadedAt, uploadedBy), never store PDF binary data in Yjs as it is too large
UseimportFromGoogleDrivefrom@api/google-drive.jsfor importing PDFs from Google Drive. Pass fileId, projectId, studyId, and tag
Save form state usingsaveFormStatefrom@/lib/formStatePersistence.jsbefore triggering OAuth redirects for Google Drive
UseaddPdfToStudyoperation fromuseProjecthook to add PDFs to a study, passing id, name, size, tag, uploadedAt, and uploadedBy
UseremovePdfFromStudyoperation fromuseProjecthook to remove PDFs from a study
UsepdfPreviewStorefrom@/stores/pdfPreviewStore.jsfor managing PDF preview state (openPreview, closePreview, getPreview methods)
UsedownloadPdffrom@api/pdf-api.jsto download PDFs from server, then cache the result usingcachePdf
Always cache PDFs after download and check cache before downloading to avoid redundant downloads
Use PDF operations fromuseProjecthook instead of bypassing through direct API calls
Files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/api/billing.jspackages/web/src/components/mocks/MockIndex.jsxpackages/web/src/lib/plan-redirect-utils.jspackages/web/src/components/settings/pages/PlansSettings.jsx
packages/web/src/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/pdf-handling.mdc)
Use
PdfViewercomponent from@/components/checklist-ui/pdf/PdfViewer.jsxfor displaying PDFs, passing pdfData as ArrayBuffer, fileName, readOnly, and onPageChange
Files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/components/mocks/MockIndex.jsxpackages/web/src/components/settings/pages/PlansSettings.jsx
{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)
{packages/web/**,packages/landing/**}/**/*.{jsx,tsx}: NEVER destructure props in SolidJS components - access props directly or wrap in functions to maintain reactivity
Use external stores in packages/web/src/stores/ for shared/cross-feature state instead of prop-drilling
Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Use Solid's Show component for conditional rendering instead of ternary operators
Use Solid's For component for rendering lists instead of Array.map()
Use the children helper when manipulating props.children in SolidJS componentsUse Show and For components for conditional and list rendering in SolidJS
Files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/components/mocks/MockIndex.jsxpackages/web/src/components/settings/pages/PlansSettings.jsxpackages/landing/src/routes/pricing.jsx
{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/solidjs.mdc)
{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: Use createSignal for simple reactive values in SolidJS components
Use createStore for complex objects and arrays that need granular reactivity in SolidJS
Use createMemo for computed/derived values that depend on reactive state in SolidJS
Always clean up SolidJS effects that create subscriptions or timers using onCleanup
Import stores directly in components and use store read/write action pattern - read from store, write via actions store
Prefer derived state with createMemo or signals over effects whenever possible
Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state
{packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx}: NEVER destructure props in SolidJS components - it breaks reactivity. Access props directly (e.g., props.name) or wrap in a function (e.g., () => props.name)
Store imports should access store data directly from external stores (packages/web/src/stores/) rather than receiving store data via props
Use createSignal for simple, reactive values in SolidJS
Use createStore for complex objects and nested state in SolidJS, with nested updates using setState pattern (e.g., setState('items', items => [...items, newItem]))
Use createMemo for derived/computed values in SolidJS to maintain reactivity
Use local createSignal or createStore for local component state
Components should receive at most 1-5 props for local configuration only
Move business logic to stores, utilities, or SolidJS primitives rather than keeping it in component code
Files:
packages/web/src/components/auth/SignUp.jsxpackages/landing/src/lib/config.jspackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/api/billing.jspackages/web/src/components/mocks/MockIndex.jsxpackages/web/src/lib/plan-redirect-utils.jspackages/web/src/components/settings/pages/PlansSettings.jsxpackages/landing/src/routes/pricing.jsx
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/yjs-sync.mdc)
**/*.{js,jsx,ts,tsx}: Use theuseProjecthook for managing Yjs connections with reference counting instead of creating Y.Doc instances directly
Never create Y.Doc instances directly; always use the connection registry managed by useProject
Access Y.Doc connection state via projectStore.getConnectionState(projectId) instead of checking connection state directly
Read Yjs-synced data from projectStore (using getStudies, getChecklist, etc.) rather than accessing Y.Doc maps/arrays directly
Use operation functions from useProject or projectActionsStore for writing data instead of directly modifying Y.Doc
Don't store Y.Doc references in component state; always retrieve the connection through useProject
Handle connection cleanup via onCleanup() in Solid.js components or equivalent cleanup patterns, calling disconnect() when the component unmounts
Files:
packages/web/src/components/auth/SignUp.jsxpackages/landing/src/lib/config.jspackages/workers/src/routes/billing/checkout.tspackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/api/billing.jspackages/web/src/components/mocks/MockIndex.jsxpackages/shared/src/plans/index.tspackages/shared/src/plans/catalog.tspackages/web/src/lib/plan-redirect-utils.jspackages/web/src/components/settings/pages/PlansSettings.jsxpackages/landing/src/routes/pricing.jsx
packages/web/src/**
📄 CodeRabbit inference engine (.cursor/rules/organizations.mdc)
packages/web/src/**: Use human-readable slug in frontend URLs: /orgs/:orgSlug/...
Use orgId (UUID) for API calls, not orgSlug, when making fetch requests to backend endpoints
Files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/api/billing.jspackages/web/src/components/mocks/MockIndex.jsxpackages/web/src/lib/plan-redirect-utils.jspackages/web/src/components/settings/pages/PlansSettings.jsx
packages/web/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)
packages/web/**/*.{js,ts,jsx,tsx}: UseapiFetchfor all frontend API calls instead of raw fetch to automatically handle JSON parsing, errors, and toast notifications
UsehandleFetchErrorfor wrapping legacy raw fetch calls in frontend code (legacy pattern, prefer apiFetch)
Use error utility functions likeisErrorCodefrom error-utils to check specific error types instead of direct string comparisons
Files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/api/billing.jspackages/web/src/components/mocks/MockIndex.jsxpackages/web/src/lib/plan-redirect-utils.jspackages/web/src/components/settings/pages/PlansSettings.jsx
packages/web/**/*.{jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)
Use
createFormErrorSignalsfor form error handling in frontend forms to manage field-level and global error states
Files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/components/mocks/MockIndex.jsxpackages/web/src/components/settings/pages/PlansSettings.jsx
packages/{web,workers}/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)
Never throw string literals; always throw Error objects or return domain errors from API routes
Files:
packages/web/src/components/auth/SignUp.jsxpackages/workers/src/routes/billing/checkout.tspackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/api/billing.jspackages/web/src/components/mocks/MockIndex.jsxpackages/web/src/lib/plan-redirect-utils.jspackages/web/src/components/settings/pages/PlansSettings.jsx
{packages/web/src/stores/**/*.{js,jsx,ts,tsx},packages/web/src/**/*.{js,jsx,ts,tsx}}
📄 CodeRabbit inference engine (.github/instructions/solidjs.instructions.md)
Shared or cross-feature state should use external stores located in packages/web/src/stores/
Files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/api/billing.jspackages/web/src/components/mocks/MockIndex.jsxpackages/web/src/lib/plan-redirect-utils.jspackages/web/src/components/settings/pages/PlansSettings.jsx
**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)
**/*.{js,ts,jsx,tsx}: Prefer modern ES6+ syntax and features
Comments should explain why something is being done, not what the code is doing
Reserve comments for explaining intent, context, workarounds, assumptions, edge cases, or limitations
Use TODO(agent): prefix for incomplete work or flagged items with brief description and relevant doc references
Files:
packages/web/src/components/auth/SignUp.jsxpackages/landing/src/lib/config.jspackages/workers/src/routes/billing/checkout.tspackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/api/billing.jspackages/web/src/components/mocks/MockIndex.jsxpackages/shared/src/plans/index.tspackages/shared/src/plans/catalog.tspackages/web/src/lib/plan-redirect-utils.jspackages/web/src/components/settings/pages/PlansSettings.jsxpackages/landing/src/routes/pricing.jsx
**/*
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Never use emojis or unicode symbols anywhere in code, comments, documentation, plan files, commit messages, or examples
Files:
packages/web/src/components/auth/SignUp.jsxpackages/landing/src/lib/config.jspackages/workers/src/routes/billing/checkout.tspackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/api/billing.jspackages/web/src/components/mocks/MockIndex.jsxpackages/shared/src/plans/index.tspackages/shared/src/plans/catalog.tspackages/web/src/lib/plan-redirect-utils.jspackages/web/src/components/settings/pages/PlansSettings.jsxpackages/landing/src/routes/pricing.jsx
packages/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/**/*.{ts,tsx,js,jsx}: Prefer modern ES6+ syntax and features
Prefer config files over hardcoding values
Keep files small, focused, and modular - extract large files into sub-modules or separate utilities
Each file should handle one coherent responsibility
Comments should explain WHY something is being done or provide context, not repeat what the code is saying
Do NOT narrate what code is doing, don't duplicate function/variable names in comments, and don't leave stale comments that contradict the code
Use the Agent TODO convention// TODO(agent): Brief descriptionfor incomplete work, flagging items for future attention, known limitations, and documentation section referencesUse TODO(agent) convention with brief description and relevant doc section references for incomplete work or flagged issues
Files:
packages/web/src/components/auth/SignUp.jsxpackages/landing/src/lib/config.jspackages/workers/src/routes/billing/checkout.tspackages/web/src/components/auth/CompleteProfile.jsxpackages/web/src/components/auth/AuthLayout.jsxpackages/web/src/api/billing.jspackages/web/src/components/mocks/MockIndex.jsxpackages/shared/src/plans/index.tspackages/shared/src/plans/catalog.tspackages/web/src/lib/plan-redirect-utils.jspackages/web/src/components/settings/pages/PlansSettings.jsxpackages/landing/src/routes/pricing.jsx
packages/workers/src/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/workers.mdc)
ALWAYS use
db.batch()for multiple related database operations in Drizzle to ensure atomicity - all operations must succeed or all fail together
Files:
packages/workers/src/routes/billing/checkout.ts
packages/workers/src/routes/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (.cursor/rules/workers.mdc)
packages/workers/src/routes/**/*.{js,ts,jsx,tsx}: ALWAYS validate request bodies usingvalidateRequestmiddleware from the validation config rather than manual validation
UsevalidateQueryParamsmiddleware for validating query parameters in routes
Files:
packages/workers/src/routes/billing/checkout.ts
packages/workers/**/*.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/error-handling.mdc)
packages/workers/**/*.{js,ts}: Always usecreateDomainErrorfrom@corates/sharedfor backend error handling instead of manually creating error objects
Use predefined error constants from@corates/shared(PROJECT_ERRORS, AUTH_ERRORS, VALIDATION_ERRORS, SYSTEM_ERRORS, USER_ERRORS) instead of arbitrary error strings
Wrap database operations in try-catch blocks and return domain errors using createDomainError with SYSTEM_ERRORS.DB_ERROR
Use validation middleware (validateRequest) for request validation instead of manually validating request bodies in routes
Files:
packages/workers/src/routes/billing/checkout.ts
packages/workers/**/routes/**/*.{js,ts}
📄 CodeRabbit inference engine (.github/instructions/api-routes.instructions.md)
packages/workers/**/routes/**/*.{js,ts}: Always usevalidateRequestmiddleware for request body validation in API routes, imported from '../config/validation.js'
Create DB client from environment usingcreateDb(c.env.DB)instead of creating DB connections directly
Usedb.batch()for atomic database operations when performing multiple related inserts or updates
Use error helpers from@corates/shared(notFound,forbidden,badRequest, etc.) for error handling in API routes
Files:
packages/workers/src/routes/billing/checkout.ts
packages/workers/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/corates.mdc)
packages/workers/src/**/*.{ts,tsx}: Use Zod for schema and input validation on backend services
Use Drizzle ORM for all database interactions and migrations
Use Better-Auth for authentication and user management
All project routes are org-scoped and use/api/orgs/:orgId/projects/...pattern in backend
UserequireOrgMembershipandrequireProjectAccessmiddleware for authentication in org-scoped routes
Never bypass Drizzle for database accessNever bypass Drizzle for database access - use Drizzle ORM for all database operations
Files:
packages/workers/src/routes/billing/checkout.ts
packages/workers/src/**/*.ts
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
packages/workers/src/**/*.ts: Use Zod for schema and input validation in backend code
Use Drizzle ORM for ALL database interactions and migrations
Use Better-Auth for authentication and user management
Never bypass Drizzle for database access
Files:
packages/workers/src/routes/billing/checkout.ts
🧠 Learnings (34)
📚 Learning: 2025-12-27T03:01:54.727Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/form-state.mdc:0-0
Timestamp: 2025-12-27T03:01:54.727Z
Learning: Applies to packages/web/src/**/*.{js,jsx} : Add restore parameters to the URL after OAuth redirects in the format '?restore=<formType>&projectId=<projectId>' to signal form state restoration on mount
Applied to files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/AuthLayout.jsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Save form state using `saveFormState` from `@/lib/formStatePersistence.js` before triggering OAuth redirects for Google Drive
Applied to files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/AuthLayout.jsx
📚 Learning: 2025-12-27T03:01:54.727Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/form-state.mdc:0-0
Timestamp: 2025-12-27T03:01:54.727Z
Learning: Applies to packages/web/src/**/*.{js,jsx} : Restore form state after OAuth redirects by checking URL restore params with getRestoreParamsFromUrl(), retrieving saved state with getFormState(), restoring to form, clearing saved state with clearFormState(), and clearing URL params with clearRestoreParamsFromUrl()
Applied to files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/AuthLayout.jsx
📚 Learning: 2026-01-17T00:25:12.507Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use local createSignal or createStore for local component state
Applied to files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/AuthLayout.jsx
📚 Learning: 2025-12-27T03:01:54.727Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/form-state.mdc:0-0
Timestamp: 2025-12-27T03:01:54.727Z
Learning: Applies to packages/web/src/**/*.{js,jsx} : Save form state to IndexedDB before initiating OAuth redirects (Google Drive, ORCID) using saveFormState() with form type ('createProject' or 'addStudies') and serializable state only
Applied to files:
packages/web/src/components/auth/SignUp.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use local createSignal or createStore for local component state; use external stores for shared/cross-feature state
Applied to files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/AuthLayout.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Create reusable logic in primitives (hooks) in packages/web/src/primitives/ and import them into components
Applied to files:
packages/web/src/components/auth/SignUp.jsxpackages/web/src/components/auth/AuthLayout.jsx
📚 Learning: 2025-12-27T03:01:54.727Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/form-state.mdc:0-0
Timestamp: 2025-12-27T03:01:54.727Z
Learning: Applies to packages/web/src/**/*.{js,jsx} : Form state persistence library functions (saveFormState, getFormState, clearFormState, getRestoreParamsFromUrl, clearRestoreParamsFromUrl) are implemented in packages/web/src/lib/formStatePersistence.js and should be imported from `@/lib/formStatePersistence.js`
Applied to files:
packages/web/src/components/auth/SignUp.jsx
📚 Learning: 2026-01-06T23:56:57.354Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/error-handling.mdc:0-0
Timestamp: 2026-01-06T23:56:57.354Z
Learning: Applies to packages/web/**/*.{jsx,tsx} : Use `createFormErrorSignals` for form error handling in frontend forms to manage field-level and global error states
Applied to files:
packages/web/src/components/auth/SignUp.jsx
📚 Learning: 2026-01-01T23:32:23.488Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers.mdc:0-0
Timestamp: 2026-01-01T23:32:23.488Z
Learning: Applies to packages/workers/src/routes/orgs/**/*.{js,ts,jsx,tsx} : Use `getOrgContext(c)` to retrieve the validated orgId from context instead of manual checks - orgId is guaranteed valid by middleware
Applied to files:
packages/workers/src/routes/billing/checkout.ts
📚 Learning: 2026-01-01T23:32:23.488Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers.mdc:0-0
Timestamp: 2026-01-01T23:32:23.488Z
Learning: Applies to packages/workers/src/routes/orgs/**/*.{js,ts,jsx,tsx} : Use `requireOrgMembership` and `requireProjectAccess` middleware instead of manual membership checks for org-scoped routes
Applied to files:
packages/workers/src/routes/billing/checkout.ts
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/workers/src/**/*.{ts,tsx} : Use `requireOrgMembership` and `requireProjectAccess` middleware for authentication in org-scoped routes
Applied to files:
packages/workers/src/routes/billing/checkout.ts
📚 Learning: 2026-01-01T23:31:43.756Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2026-01-01T23:31:43.756Z
Learning: Applies to packages/workers/src/routes/orgs/**/*.js : Use organization-scoped route patterns with path structure `/api/orgs/:orgId/...` and leverage `requireOrgMembership`, `requireProjectAccess`, `getOrgContext`, and `getProjectContext` middleware
Applied to files:
packages/workers/src/routes/billing/checkout.ts
📚 Learning: 2026-01-17T00:25:03.925Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/api-routes.instructions.md:0-0
Timestamp: 2026-01-17T00:25:03.925Z
Learning: Applies to packages/workers/**/routes/**/*.{js,ts} : Always use `validateRequest` middleware for request body validation in API routes, imported from '../config/validation.js'
Applied to files:
packages/workers/src/routes/billing/checkout.ts
📚 Learning: 2026-01-17T00:25:35.702Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/corates.mdc:0-0
Timestamp: 2026-01-17T00:25:35.702Z
Learning: Applies to packages/workers/src/**/*.{ts,tsx} : Use Drizzle ORM for all database interactions and migrations
Applied to files:
packages/workers/src/routes/billing/checkout.ts
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/workers/src/**/*.ts : Use Drizzle ORM for ALL database interactions and migrations
Applied to files:
packages/workers/src/routes/billing/checkout.ts
📚 Learning: 2026-01-17T00:25:03.925Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/api-routes.instructions.md:0-0
Timestamp: 2026-01-17T00:25:03.925Z
Learning: Applies to packages/workers/**/routes/**/*.{js,ts} : Create DB client from environment using `createDb(c.env.DB)` instead of creating DB connections directly
Applied to files:
packages/workers/src/routes/billing/checkout.ts
📚 Learning: 2026-01-01T23:31:43.756Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2026-01-01T23:31:43.756Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always create DB client from environment using `createDb` function in route handlers
Applied to files:
packages/workers/src/routes/billing/checkout.ts
📚 Learning: 2026-01-01T23:31:43.756Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2026-01-01T23:31:43.756Z
Learning: Applies to packages/workers/src/routes/**/*.js : Always use Drizzle ORM with query builders - never use raw SQL in database operations
Applied to files:
packages/workers/src/routes/billing/checkout.ts
📚 Learning: 2026-01-01T23:32:23.488Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers.mdc:0-0
Timestamp: 2026-01-01T23:32:23.488Z
Learning: Applies to packages/workers/src/**/*.{js,ts,jsx,tsx} : ALWAYS use `db.batch()` for multiple related database operations in Drizzle to ensure atomicity - all operations must succeed or all fail together
Applied to files:
packages/workers/src/routes/billing/checkout.ts
📚 Learning: 2026-01-17T16:10:07.519Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-17T16:10:07.519Z
Learning: Applies to packages/workers/src/**/*.{ts,tsx} : Never bypass Drizzle for database access - use Drizzle ORM for all database operations
Applied to files:
packages/workers/src/routes/billing/checkout.ts
📚 Learning: 2026-01-01T23:31:43.756Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/api-routes.mdc:0-0
Timestamp: 2026-01-01T23:31:43.756Z
Learning: Applies to packages/workers/src/routes/**/*.js : ALWAYS use `createDomainError` from `corates/shared` for error handling in API routes
Applied to files:
packages/workers/src/routes/billing/checkout.ts
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createSignal for simple reactive values in SolidJS components
Applied to files:
packages/web/src/components/auth/AuthLayout.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Prefer derived state with createMemo or signals over effects whenever possible
Applied to files:
packages/web/src/components/auth/AuthLayout.jsx
📚 Learning: 2026-01-17T00:25:12.507Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{js,jsx,ts,tsx} : Use createSignal for simple, reactive values in SolidJS
Applied to files:
packages/web/src/components/auth/AuthLayout.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Keep SolidJS components lean and focused on rendering - move business logic to stores, primitives, or utilities
Applied to files:
packages/web/src/components/auth/AuthLayout.jsxpackages/web/src/components/mocks/MockIndex.jsx
📚 Learning: 2026-01-17T00:25:12.507Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/instructions/solidjs.instructions.md:0-0
Timestamp: 2026-01-17T00:25:12.507Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Show and For components for conditional and list rendering in SolidJS
Applied to files:
packages/web/src/components/auth/AuthLayout.jsxpackages/web/src/components/settings/pages/PlansSettings.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Solid's Show component for conditional rendering instead of ternary operators
Applied to files:
packages/web/src/components/auth/AuthLayout.jsxpackages/web/src/components/settings/pages/PlansSettings.jsx
📚 Learning: 2026-01-17T16:09:36.904Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-17T16:09:36.904Z
Learning: Applies to packages/{web,landing}/src/**/*.{ts,tsx} : Use `createMemo` for derived values in SolidJS
Applied to files:
packages/web/src/components/auth/AuthLayout.jsx
📚 Learning: 2026-01-05T01:00:58.113Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/workers-testing.mdc:0-0
Timestamp: 2026-01-05T01:00:58.113Z
Learning: Applies to {packages/workers/src/**/*.test.js,packages/workers/src/**/__tests__/**/*.js} : Only re-mock Postmark and Stripe modules inside a test file if you need behavior different from the global mock setup in packages/workers/src/__tests__/setup.js
Applied to files:
packages/web/src/components/mocks/MockIndex.jsx
📚 Learning: 2025-12-27T03:02:05.951Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/pdf-handling.mdc:0-0
Timestamp: 2025-12-27T03:02:05.951Z
Learning: Applies to packages/web/src/**/*.{js,jsx,ts,tsx} : Use `removePdfFromStudy` operation from `useProject` hook to remove PDFs from a study
Applied to files:
packages/web/src/components/mocks/MockIndex.jsx
📚 Learning: 2025-12-27T03:01:06.933Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/checklist-operations.mdc:0-0
Timestamp: 2025-12-27T03:01:06.933Z
Learning: Applies to packages/web/src/components/checklist-ui/**,packages/web/src/lib/checklist-domain.js : Register and retrieve checklists using checklist-registry: getChecklistType and getChecklistComponent functions
Applied to files:
packages/shared/src/plans/index.ts
📚 Learning: 2025-12-27T03:01:54.727Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/form-state.mdc:0-0
Timestamp: 2025-12-27T03:01:54.727Z
Learning: Applies to packages/web/src/components/project-ui/**/*.{js,jsx} : Form state should include serializable metadata when handling files (name, size, type) and use the store's pendingPdfs pattern for actual File objects that persist across redirects
Applied to files:
packages/web/src/components/settings/pages/PlansSettings.jsx
📚 Learning: 2025-12-27T03:02:26.947Z
Learnt from: CR
Repo: InfinityBowman/corates PR: 0
File: .cursor/rules/solidjs.mdc:0-0
Timestamp: 2025-12-27T03:02:26.947Z
Learning: Applies to {packages/web/**,packages/landing/**}/**/*.{jsx,tsx} : Use Solid's For component for rendering lists instead of Array.map()
Applied to files:
packages/web/src/components/settings/pages/PlansSettings.jsx
🧬 Code graph analysis (9)
packages/web/src/components/auth/SignUp.jsx (1)
packages/web/src/lib/plan-redirect-utils.js (1)
capturePlanParams(66-81)
packages/landing/src/lib/config.js (2)
packages/web/src/lib/plan-redirect-utils.js (1)
plan(89-89)packages/workers/scripts/setup-stripe-test.mjs (1)
interval(330-330)
packages/workers/src/routes/billing/checkout.ts (1)
packages/workers/src/lib/billingResolver.ts (1)
resolveOrgAccess(72-211)
packages/web/src/components/auth/CompleteProfile.jsx (1)
packages/web/src/lib/plan-redirect-utils.js (5)
hasPendingPlan(105-111)handlePendingPlanRedirect(169-246)BILLING_MESSAGES(28-49)BILLING_MESSAGES(28-49)clearPendingPlan(116-123)
packages/web/src/components/auth/AuthLayout.jsx (1)
packages/web/src/lib/plan-redirect-utils.js (2)
capturePlanParams(66-81)hasPendingPlan(105-111)
packages/web/src/api/billing.js (1)
packages/web/src/lib/apiFetch.js (1)
apiFetch(134-253)
packages/shared/src/plans/catalog.ts (1)
packages/shared/src/plans/index.ts (3)
CHECKOUT_ELIGIBLE_TIERS(32-32)BillingCatalogTier(18-18)getBillingPlanCatalog(32-32)
packages/web/src/lib/plan-redirect-utils.js (2)
packages/shared/src/plans/index.ts (1)
CHECKOUT_ELIGIBLE_TIERS(32-32)packages/web/src/api/billing.js (3)
startTrial(92-95)redirectToSingleProjectCheckout(72-76)redirectToCheckout(43-47)
packages/landing/src/routes/pricing.jsx (1)
packages/landing/src/lib/config.js (2)
urls(8-19)urls(8-19)
⏰ 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). (1)
- GitHub Check: Workers Builds: corates
🔇 Additional comments (25)
packages/landing/src/routes/pricing.jsx (1)
51-58: LGTM!The updated
getButtonUrlcorrectly passes the billing interval for subscription plans while omitting it for trial and single-project flows. The reactive signal access (billingInterval()) is properly placed within the function body.packages/shared/src/plans/catalog.ts (1)
149-156: LGTM!The derived constant correctly filters eligible tiers and provides a useful validation surface for the plan-redirect flow.
packages/shared/src/plans/index.ts (1)
32-32: LGTM!The re-export correctly extends the public API surface for the plans module.
packages/landing/src/lib/config.js (1)
10-16: LGTM!Clean implementation using
URLSearchParams. The optionalintervalparameter maintains backwards compatibility while enabling the new billing interval flow.packages/web/src/api/billing.js (3)
24-26: LGTM!Good addition of the options parameter with JSDoc documentation.
43-47: LGTM!Defaulting
showToast: falsefor redirect flows is sensible since the page navigates away before users would see the toast. The spread pattern allows callers to override if needed.
63-65: LGTM!Consistent options pattern across all checkout and trial functions.
Also applies to: 72-75, 92-95
packages/web/src/lib/plan-redirect-utils.js (6)
1-9: LGTM!Clean module header with well-organized imports separating shared config, billing API, and UI utilities.
11-49: LGTM!Well-structured constants with centralized storage keys and user-friendly toast messages. Good separation of concerns.
51-81: LGTM!
validateIntervalprovides safe normalization with a sensible default.capturePlanParamsproperly validates againstCHECKOUT_ELIGIBLE_TIERSand handles localStorage errors gracefully.
83-123: LGTM!Defensive localStorage access with try-catch wrappers. Good use of
validateIntervalfor consistent interval normalization ingetPendingPlan.
125-156: LGTM!Error type detection functions are well-structured. Using optional chaining on
startsWith(err?.code?.startsWith?.()) provides defensive handling for unexpected error shapes.
199-245: LGTM!Error handling is well thought out:
- Specific error types get appropriate messaging and navigation
- Trial errors are cleared (non-retryable)
- Checkout errors preserve pending plan for retry (transient failures)
packages/web/src/components/auth/SignUp.jsx (2)
15-15: Plan redirect helper import is aligned with usage.
53-54: Plan params captured on mount as intended.packages/web/src/components/auth/AuthLayout.jsx (3)
4-4: Plan redirect helpers imported for new flow.
34-38: Plan params are captured before redirecting logged-in users from signup.
41-50: Pending plan routing preserves checkout intent post-login.packages/web/src/components/auth/CompleteProfile.jsx (3)
35-40: Pending-plan helpers integrated for profile completion flow.
92-98: Completed profiles now respect pending plan redirects.
253-275: Pending plan redirect handling after profile submission is well structured.packages/web/src/components/settings/pages/PlansSettings.jsx (4)
7-17: Imports cover the new redirect state flow.
83-107: Pending plan processing flow is clear and deterministic.
108-113: On-mount processing of pending plans is consistent with the redirect flow.
125-219: Conditional UI states are clean and readable.
✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.
| // Check if user is already on this plan | ||
| const currentBilling = await resolveOrgAccess(db, orgId!); | ||
| if (currentBilling.effectivePlanId === tier) { | ||
| const error = createDomainError( | ||
| VALIDATION_ERRORS.INVALID_INPUT, | ||
| { | ||
| reason: 'already_on_plan', | ||
| currentPlan: tier, | ||
| }, | ||
| `You are already subscribed to the ${tier} plan.`, | ||
| ); | ||
| return c.json(error, error.statusCode as ContentfulStatusCode); | ||
| } |
There was a problem hiding this comment.
Avoid blocking interval changes or grant-to-paid upgrades
The new guard blocks any checkout when effectivePlanId === tier, even if the org is on a grant/trial or is switching interval on the same plan. That can prevent legitimate upgrades (e.g., monthly → yearly) and conversions from grants to paid subscriptions. Please scope this check to active subscriptions and include interval matching.
Proposed fix
- const currentBilling = await resolveOrgAccess(db, orgId!);
- if (currentBilling.effectivePlanId === tier) {
+ const currentBilling = await resolveOrgAccess(db, orgId!);
+ const isSameActiveSubscription =
+ currentBilling.source === 'subscription' &&
+ currentBilling.effectivePlanId === tier &&
+ currentBilling.subscription?.interval === interval;
+ if (isSameActiveSubscription) {
const error = createDomainError(
VALIDATION_ERRORS.INVALID_INPUT,
{
reason: 'already_on_plan',
currentPlan: tier,
},
`You are already subscribed to the ${tier} plan.`,
);
return c.json(error, error.statusCode as ContentfulStatusCode);
}🤖 Prompt for AI Agents
In `@packages/workers/src/routes/billing/checkout.ts` around lines 279 - 291, The
current guard uses resolveOrgAccess and blocks checkout whenever
currentBilling.effectivePlanId === tier, which wrongly prevents interval-only
changes and upgrades from grant/trial to paid; update the check so it only
short-circuits when the org has an active paid subscription with the same plan
AND the billing interval also matches. Specifically, in the checkout flow around
resolveOrgAccess/currentBilling, refine the condition to verify the subscription
is active (e.g., status or isActive flag on currentBilling) and compare both
currentBilling.effectivePlanId === tier AND currentBilling.interval ===
requestedInterval (or similar request param) before returning the
"already_on_plan" error, but allow proceed when subscription is a grant/trial or
when the interval differs. Ensure you reference currentBilling, effectivePlanId,
tier, and the interval/requestedInterval variable in your condition.
Summary by CodeRabbit
New Features
Bug Fixes
Chores
✏️ Tip: You can customize this high-level summary in your review settings.