Epic/ba 2017 stripe payments#349
Conversation
Implements the first phase of the subscription checkout experience using Stripe. Includes payment method selection (saved or new), billing address input, and "Place Order" flow. Displays confirmation modal on success and error feedback on failure. Uses Stripe invoice ID as order reference. Tax calculation and Stripe UI caching will be addressed in follow-up stories. Avoids storing Stripe data in Admin.
✅ Stripe Checkout - Payment Methods Management
Description
As a User, on the BaseApp Profile Page,I would like to add, view,
update, and remove my saved payment methods in the settings menu, In
order to manage my payment options easily and ensure uninterrupted
payments.
Acceptance Criteria
Frontend
Add a Payment Methods section to the settings menu.
Display a list of saved payment methods, including:
Card type and masked card number (e.g., Visa **** 1234).
Expiration date.
"Default" label for the primary payment method.
Include actions for each payment method via a hamburger menu:
Set as Default: Mark a selected payment method as the primary option for
transactions.
Remove: Open a confirmation modal to delete the selected payment method.
Include a "+ Add Payment Method" button that:
Opens a modal for entering card details.
https://www.figma.com/design/XRD6wSl1m8Kz6XUcAy5CLp/BaseApp---WEB?node-id=7279-10539&t=wyIQdMiNfVhcs5LQ-4
Calls the backend to securely store the payment method via Stripe.
On selecting "Remove" from the dropdown:
Display a confirmation modal with the message:
"Are you sure you want to remove this payment method? If this is your
only payment method, your payments will be interrupted until a new
method is added."
Include "Cancel" and "Remove" buttons.
Trigger a backend call to delete the payment method from Stripe and
update the user's saved methods.
Backend
Use Stripe's API to retrieve the user's saved payment methods.
Ensure each method includes:
Card type, masked number, and expiration date.
Default status.
Trigger a Stripe API call to delete the selected payment method.
Handle cases where:
The removed method is the default (prompt the user to select another
default method or display a warning).
No payment methods remain (prompt the user to add a new payment method).
Set as Default
Use Stripe's API to update the default payment method for the user.
Design Link:
https://www.figma.com/design/XRD6wSl1m8Kz6XUcAy5CLp/BaseApp---WEB?node-id=7279-191921&t=EqObBvkTBQ7zD0Fr-4
Approvd
https://app.approvd.io/silverlogic/BA/stories/38019
Demo: https://www.loom.com/share/50196b35fcd04202a50742f59d8a7745
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a comprehensive Stripe payments module with components for
checkout, payment method management, and subscription confirmation
modals.
- Added support for adding, selecting, and managing payment methods,
including card input and billing address forms.
- Integrated responsive UI elements for payment flows, including modals,
dropdowns, and styled buttons.
- Provided visual feedback with new credit card icons (Visa, Mastercard,
generic) and email masking for privacy.
- Enabled product and subscription management with real-time updates and
notifications.
- Added a utility to initialize Stripe with a publishable key.
- Included a hook encapsulating Stripe API interactions for streamlined
data fetching and mutations.
- **Chores**
- Added Stripe dependencies and updated package exports to support new
payment features.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Mathieu Bouhelier <mathieubouhelier@gmail.com>
Co-authored-by: Lúcio BJ <lb@tsl.io>
Co-authored-by: Lúcio BJ <lb@tsl.com>
Description As a User, on the BaseApp Profile Page,I would like to manage my subscription plan, payment methods, and receive notifications for failed payment attempts, In order to easily update my subscription and payment details and ensure uninterrupted access. Acceptance Criteria Frontend Add a Subscription section to the settings menu. Display the following details: Current subscription plan. Plan features. Next billing date and amount. Current payment method. Include actions: Change Plan button to navigate to the subscription options page. Cancel Subscription button that opens a confirmation modal. Display a dropdown menu under Payment Method: Show all saved payment methods. Allow users to select a different payment method. If you change the payment method then display a toast informing the user that the change was successful. Include an "Add New Payment Method" option. Clicking on button displays new payment method modal from checkout story Failed payment attempts will be covered in a follow-up story On clicking Cancel Subscription, show a confirmation modal: Text: "Are you sure you want to cancel your subscription? You will retain access to your current plan until the end of the billing period. After that, your account will be downgraded to the free plan." Buttons: "Back" and "Cancel Subscription." Subscription Management Story APIs for Frontend Build an API to: fetch subscription plan details directly from Stripe or the local database (if cached). Identify the user’s active plan by querying Stripe’s Customer API. Expose endpoints for actions like subscribing, upgrading, or canceling plans, routed through Stripe’s API. User Subscription Status Use Stripe's Customer API to determine: The user's active subscription plan. Billing cycle and renewal dates. Any upcoming invoices or changes in plan. Stripe Integration Use Stripe's API to retrieve: The user's active subscription details. Billing information, including the default payment method. Select and Update Payment Method Trigger Stripe API to update the payment method for the subscription. Update the selected default payment method for future billing. Payment Attempt Notifications Trigger a Stripe API call to cancel the subscription. Ensure the user retains access until the end of the billing cycle. Downgrade the user's account to the free plan after the billing cycle ends. Backend Requirements Create an endpoint to fetch subscription details from Stripe: Include the active plan, billing amount, next payment date, and payment method(s). Keep in mind only 1 subscription can be active at a time, and we need to display the active subs. correctly Create an endpoint to update the payment method: Accept a new payment method ID and update it in Stripe. Create an endpoint to cancel subscriptions: Trigger Stripe API to cancel the subscription. Update the user's account status to reflect the cancellation. Flag failed payment attempts in the backend for display in the UI. *Database Updates Update user account details upon subscription or payment method changes. Design Link: https://www.figma.com/design/XRD6wSl1m8Kz6XUcAy5CLp/BaseApp---WEB?node-id=7279-189782&t=IMDSKi4lrWcYru1G-4 ## Installation guidance: BE: USee the master branch In FE-Template * Use the branch "wip-stripe-for-test-purpose" * Update the following value according to your configuration * subscriptionId="sub_1RYuyQGYYYYYY" * customerId="cus_Rz7mAKnYYYY" * Add "NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY:"your_value_here"" to the .env file * Then run * nvm use * pnpm replace-baseapp-catalogs * pnpm clean && pnpm i && pnpm dev * access to http://localhost:3000/checkout Demo https://www.loom.com/share/9c1b83a7a3364ce3adb43ca19ede57fa <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a comprehensive Stripe payments module, enabling subscription checkout, payment method management, and card addition via modals. - Added responsive checkout and subscription management interfaces with support for viewing, adding, and selecting payment methods. - Implemented confirmation dialogs and cancellation flows for subscriptions. - Integrated branded credit card icons (Visa, Mastercard, generic) for enhanced payment method display. - **Enhancements** - Added utility functions for masking email addresses and improved user feedback with toast notifications. - Centralized and streamlined exports for easier integration and usage. - **Styling** - Introduced new styled components for modals, buttons, and containers to ensure a consistent and responsive user interface. - **Dependencies** - Added Stripe SDK dependencies for seamless payment integration. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
As a User, on the BaseApp Profile Page,I would like to View a detailed history of my transactions and access receipts for my subscription payments, In order to Keep track of my subscription expenses, verify payments, and maintain accurate financial records. Acceptance Criteria Given the user navigates to the "Transaction History" section within settings, the user views a list of all historical payments, sorted by date with the most recent payment listed first. Use the Material UI table component Ensure the transaction history is responsive and provides a consistent user experience on desktop, tablet, and mobile devices. Design Link: https://www.figma.com/design/XRD6wSl1m8Kz6XUcAy5CLp/BaseApp---WEB?node-id=7279-192622&t=pkTsTldiYoAVvZuw-4 Approvd https://app.approvd.io/projects/BA/stories/39485 Demo https://www.loom.com/share/d293f0dec8a045a7aa1167a45d56df1d --------- Co-authored-by: Lúcio BJ <lb@tsl.com>
Implements the first phase of the subscription checkout experience using Stripe. Includes payment method selection (saved or new), billing address input, and "Place Order" flow. Displays confirmation modal on success and error feedback on failure. Uses Stripe invoice ID as order reference. Tax calculation and Stripe UI caching will be addressed in follow-up stories. Avoids storing Stripe data in Admin.
✅ Stripe Checkout - Payment Methods Management
Description
As a User, on the BaseApp Profile Page,I would like to add, view,
update, and remove my saved payment methods in the settings menu, In
order to manage my payment options easily and ensure uninterrupted
payments.
Acceptance Criteria
Frontend
Add a Payment Methods section to the settings menu.
Display a list of saved payment methods, including:
Card type and masked card number (e.g., Visa **** 1234).
Expiration date.
"Default" label for the primary payment method.
Include actions for each payment method via a hamburger menu:
Set as Default: Mark a selected payment method as the primary option for
transactions.
Remove: Open a confirmation modal to delete the selected payment method.
Include a "+ Add Payment Method" button that:
Opens a modal for entering card details.
https://www.figma.com/design/XRD6wSl1m8Kz6XUcAy5CLp/BaseApp---WEB?node-id=7279-10539&t=wyIQdMiNfVhcs5LQ-4
Calls the backend to securely store the payment method via Stripe.
On selecting "Remove" from the dropdown:
Display a confirmation modal with the message:
"Are you sure you want to remove this payment method? If this is your
only payment method, your payments will be interrupted until a new
method is added."
Include "Cancel" and "Remove" buttons.
Trigger a backend call to delete the payment method from Stripe and
update the user's saved methods.
Backend
Use Stripe's API to retrieve the user's saved payment methods.
Ensure each method includes:
Card type, masked number, and expiration date.
Default status.
Trigger a Stripe API call to delete the selected payment method.
Handle cases where:
The removed method is the default (prompt the user to select another
default method or display a warning).
No payment methods remain (prompt the user to add a new payment method).
Set as Default
Use Stripe's API to update the default payment method for the user.
Design Link:
https://www.figma.com/design/XRD6wSl1m8Kz6XUcAy5CLp/BaseApp---WEB?node-id=7279-191921&t=EqObBvkTBQ7zD0Fr-4
Approvd
https://app.approvd.io/silverlogic/BA/stories/38019
Demo: https://www.loom.com/share/50196b35fcd04202a50742f59d8a7745
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- **New Features**
- Introduced a comprehensive Stripe payments module with components for
checkout, payment method management, and subscription confirmation
modals.
- Added support for adding, selecting, and managing payment methods,
including card input and billing address forms.
- Integrated responsive UI elements for payment flows, including modals,
dropdowns, and styled buttons.
- Provided visual feedback with new credit card icons (Visa, Mastercard,
generic) and email masking for privacy.
- Enabled product and subscription management with real-time updates and
notifications.
- Added a utility to initialize Stripe with a publishable key.
- Included a hook encapsulating Stripe API interactions for streamlined
data fetching and mutations.
- **Chores**
- Added Stripe dependencies and updated package exports to support new
payment features.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Mathieu Bouhelier <mathieubouhelier@gmail.com>
Co-authored-by: Lúcio BJ <lb@tsl.io>
Co-authored-by: Lúcio BJ <lb@tsl.com>
Description As a User, on the BaseApp Profile Page,I would like to manage my subscription plan, payment methods, and receive notifications for failed payment attempts, In order to easily update my subscription and payment details and ensure uninterrupted access. Acceptance Criteria Frontend Add a Subscription section to the settings menu. Display the following details: Current subscription plan. Plan features. Next billing date and amount. Current payment method. Include actions: Change Plan button to navigate to the subscription options page. Cancel Subscription button that opens a confirmation modal. Display a dropdown menu under Payment Method: Show all saved payment methods. Allow users to select a different payment method. If you change the payment method then display a toast informing the user that the change was successful. Include an "Add New Payment Method" option. Clicking on button displays new payment method modal from checkout story Failed payment attempts will be covered in a follow-up story On clicking Cancel Subscription, show a confirmation modal: Text: "Are you sure you want to cancel your subscription? You will retain access to your current plan until the end of the billing period. After that, your account will be downgraded to the free plan." Buttons: "Back" and "Cancel Subscription." Subscription Management Story APIs for Frontend Build an API to: fetch subscription plan details directly from Stripe or the local database (if cached). Identify the user’s active plan by querying Stripe’s Customer API. Expose endpoints for actions like subscribing, upgrading, or canceling plans, routed through Stripe’s API. User Subscription Status Use Stripe's Customer API to determine: The user's active subscription plan. Billing cycle and renewal dates. Any upcoming invoices or changes in plan. Stripe Integration Use Stripe's API to retrieve: The user's active subscription details. Billing information, including the default payment method. Select and Update Payment Method Trigger Stripe API to update the payment method for the subscription. Update the selected default payment method for future billing. Payment Attempt Notifications Trigger a Stripe API call to cancel the subscription. Ensure the user retains access until the end of the billing cycle. Downgrade the user's account to the free plan after the billing cycle ends. Backend Requirements Create an endpoint to fetch subscription details from Stripe: Include the active plan, billing amount, next payment date, and payment method(s). Keep in mind only 1 subscription can be active at a time, and we need to display the active subs. correctly Create an endpoint to update the payment method: Accept a new payment method ID and update it in Stripe. Create an endpoint to cancel subscriptions: Trigger Stripe API to cancel the subscription. Update the user's account status to reflect the cancellation. Flag failed payment attempts in the backend for display in the UI. *Database Updates Update user account details upon subscription or payment method changes. Design Link: https://www.figma.com/design/XRD6wSl1m8Kz6XUcAy5CLp/BaseApp---WEB?node-id=7279-189782&t=IMDSKi4lrWcYru1G-4 ## Installation guidance: BE: USee the master branch In FE-Template * Use the branch "wip-stripe-for-test-purpose" * Update the following value according to your configuration * subscriptionId="sub_1RYuyQGYYYYYY" * customerId="cus_Rz7mAKnYYYY" * Add "NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY:"your_value_here"" to the .env file * Then run * nvm use * pnpm replace-baseapp-catalogs * pnpm clean && pnpm i && pnpm dev * access to http://localhost:3000/checkout Demo https://www.loom.com/share/9c1b83a7a3364ce3adb43ca19ede57fa <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a comprehensive Stripe payments module, enabling subscription checkout, payment method management, and card addition via modals. - Added responsive checkout and subscription management interfaces with support for viewing, adding, and selecting payment methods. - Implemented confirmation dialogs and cancellation flows for subscriptions. - Integrated branded credit card icons (Visa, Mastercard, generic) for enhanced payment method display. - **Enhancements** - Added utility functions for masking email addresses and improved user feedback with toast notifications. - Centralized and streamlined exports for easier integration and usage. - **Styling** - Introduced new styled components for modals, buttons, and containers to ensure a consistent and responsive user interface. - **Dependencies** - Added Stripe SDK dependencies for seamless payment integration. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
As a User, on the BaseApp Profile Page,I would like to View a detailed history of my transactions and access receipts for my subscription payments, In order to Keep track of my subscription expenses, verify payments, and maintain accurate financial records. Acceptance Criteria Given the user navigates to the "Transaction History" section within settings, the user views a list of all historical payments, sorted by date with the most recent payment listed first. Use the Material UI table component Ensure the transaction history is responsive and provides a consistent user experience on desktop, tablet, and mobile devices. Design Link: https://www.figma.com/design/XRD6wSl1m8Kz6XUcAy5CLp/BaseApp---WEB?node-id=7279-192622&t=pkTsTldiYoAVvZuw-4 Approvd https://app.approvd.io/projects/BA/stories/39485 Demo https://www.loom.com/share/d293f0dec8a045a7aa1167a45d56df1d --------- Co-authored-by: Lúcio BJ <lb@tsl.com>
…/baseapp-frontend into epic/BA-2017-stripe-payments
After a major refactor in the BE, this changes were necessary to align with it, and make sure the flow is working as intended --------- Co-authored-by: Lúcio BJ <lb@tsl.com>
✅ Stripe Checkout - Subscription Plans Page
Description
As a User, on the BaseApp Profile Page,I would like to view and manage
available subscription plans on a responsive page tailored for different
devices, In order to select or manage a plan that meets my needs
effectively.
Acceptance Criteria
Frontend
Responsive Layouts
Desktop (Horizontal layout):
Display plans in a row, account for multiple rows if more than 3 plans
exist.
Ensure proper alignment and consistent card widths regardless of the
number of plans.
Mobile & Tablet (Vertical layout):
Stack plans vertically to ensure usability and readability on smaller
screens.
CoP comments
Its ok to reuse Stripe Web Elements.
Check if its feasible to replace the BaseApp Components with the Stripe
Payment Elements
Check so that we can customize the individual component so that the end
result is as similar as possible to BaseApp components in the designs:
https://docs.stripe.com/elements/appearance-api#rules
We have to think about what customizations are we going to be enabled so
that components can be re used in other projects.
Probably a good idea to talk to CoP when thinking of this.
Plan Cards
Reusable plan card component:
Title, pricing, description, and features dynamically populated via
backend.
Call-to-Action Buttons
Highlight the “Subscribe” button for available plans.
Use of the button to redirect user to Checkout Page will be covered in a
follow-up story
Design Link:
https://www.figma.com/design/XRD6wSl1m8Kz6XUcAy5CLp/BaseApp---WEB?node-id=7170-121730&t=EqObBvkTBQ7zD0Fr-4
Approvd
https://app.approvd.io/projects/BA/stories/38018
Demo: https://www.loom.com/share/b024bb2566c34682bd0c182616248275
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
- New Features
- Browse available subscriptions, toggle monthly/yearly, view pricing,
features, and active status.
- Complete Stripe-based checkout: address entry, payment method
selection/addition, error handling, and success confirmation.
- Manage payment methods: list, set default, remove, and add new via
modal.
- Subscription management: view plan details, change plan, update
default payment method, and cancel with confirmation; includes Free plan
view.
- Invoices table with pagination, mobile-friendly rows, and receipt
links.
- New payment card icons (Visa/Mastercard) and improved dialog with
optional hidden Cancel button.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
---------
Co-authored-by: Mathieu Bouhelier <mathieubouhelier@gmail.com>
Co-authored-by: Lúcio BJ <lb@tsl.io>
Co-authored-by: Lúcio BJ <lb@tsl.com>
Co-authored-by: Alisson Patricio <eu@alisson.net>
Co-authored-by: Mathieu Bouhelier <62220320+mathieubouhelier@users.noreply.github.com>
❌ Subscription Page Component Integration Description fields Description Acceptance Criteria Implement components to the subscription page so that everything is integrated with existing components in baseapp. Approvd https://app.approvd.io/silverlogic/BA/stories/42883 Demo: https://www.loom.com/share/0f76db0716b6466ebae525250630c2c6 --------- Co-authored-by: Mathieu Bouhelier <mathieubouhelier@gmail.com> Co-authored-by: Lúcio BJ <lb@tsl.io> Co-authored-by: Lúcio BJ <lb@tsl.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a complete web payments module: Stripe API wrapper, React Query hooks, Stripe Elements flows (add card, checkout, subscription management), invoice UI, utilities/icons/styled components, and package export mappings for payments/web. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant UI as CheckoutComponent
participant Dropdown as PaymentDropdown
participant Modal as AddCardModal
participant Elements as Stripe Elements
participant API as StripeApi/Backend
User->>UI: Open checkout page
UI->>API: listProducts(), getCustomer(), listPaymentMethods()
API-->>UI: products, customer, paymentMethods
User->>Dropdown: Choose "Add New" card
Dropdown->>API: createSetupIntent(entityId)
API-->>Dropdown: setupIntent.client_secret
Dropdown->>Modal: open(clientSecret)
User->>Elements: Fill PaymentElement & AddressElement
User->>Modal: Confirm
Modal->>API: confirmSetup (via Stripe or backend)
API-->>Modal: payment_method id
Modal->>UI: handleSetupSuccess(payment_method)
User->>UI: Click Subscribe
UI->>API: createSubscription(productId, paymentMethodId)
API-->>UI: subscription created
UI->>UI: invalidate queries, show confirmation modal
sequenceDiagram
participant User
participant SubMgmt as SubscriptionManagement
participant Dropdown as PaymentDropdown
participant API as StripeApi/Backend
User->>SubMgmt: Load subscription management
SubMgmt->>API: getSubscription(), listPaymentMethods()
API-->>SubMgmt: subscription, paymentMethods
User->>Dropdown: Select different default method
Dropdown->>API: updatePaymentMethod / updateSubscription
API-->>Dropdown: success
Dropdown->>SubMgmt: trigger refetch/invalidate
User->>SubMgmt: Click Cancel
SubMgmt->>User: show CancelSubscriptionModal
User->>SubMgmt: Confirm cancel
SubMgmt->>API: cancelSubscription(subscriptionId)
API-->>SubMgmt: canceled
SubMgmt->>SubMgmt: invalidate subscription, update UI
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 3
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
🟠 Major comments (20)
packages/components/modules/payments/web/SubscriptionManagement/utils.ts-1-20 (1)
1-20:⚠️ Potential issue | 🟠 Major
incompleteis misclassified as "Active" andpausedstatus is unhandled.Stripe treats
incompleteas a pending-payment state (first invoice not yet successfully paid), not an active subscription. The current mapping incorrectly shows pending subscriptions as "Active" with green success color. Additionally,pausedis a valid Stripe subscription status (occurs when trial ends without a default payment method, or via the Pause API), but the function returns blank{ label: '', color: '' }for it, leaving a valid state unlabeled in the UI.Suggested fix
-export const getChipLabelAndColorByStatus = (status: string) => { - let label = '' - let color: 'success' | 'error' | 'warning' | 'info' | '' = '' - if (status === 'active' || status === 'trialing' || status === 'incomplete') { - label = 'Active' - color = 'success' - } - if (status === 'canceled' || status === 'incomplete_expired') { - label = 'Canceled' - color = 'error' - } - if (status === 'past_due') { - label = 'Past Due' - color = 'warning' - } - if (status === 'unpaid') { - label = 'Unpaid' - color = 'error' - } - return { label, color } -} +type SubscriptionStatus = + | 'active' + | 'trialing' + | 'incomplete' + | 'incomplete_expired' + | 'past_due' + | 'canceled' + | 'unpaid' + | 'paused' + +export const getChipLabelAndColorByStatus = (status: SubscriptionStatus) => { + switch (status) { + case 'active': + case 'trialing': + return { label: 'Active', color: 'success' as const } + case 'incomplete': + return { label: 'Pending Payment', color: 'warning' as const } + case 'incomplete_expired': + return { label: 'Expired', color: 'error' as const } + case 'past_due': + return { label: 'Past Due', color: 'warning' as const } + case 'canceled': + return { label: 'Canceled', color: 'error' as const } + case 'unpaid': + return { label: 'Unpaid', color: 'error' as const } + case 'paused': + return { label: 'Paused', color: 'info' as const } + default: + return { label: 'Unknown', color: 'info' as const } + } +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/SubscriptionManagement/utils.ts` around lines 1 - 20, The getChipLabelAndColorByStatus function incorrectly classifies 'incomplete' as Active and omits 'paused'; update getChipLabelAndColorByStatus so statuses are matched exclusively (use if/else-if or a switch) and map 'incomplete' to a pending state (e.g., label "Pending" with color 'warning' or 'info') and add handling for 'paused' (e.g., label "Paused" with color 'info' or 'warning'); keep existing mappings for 'active', 'trialing', 'canceled', 'past_due', and 'unpaid' but ensure no overlapping branches cause wrong labels.packages/design-system/components/web/icons/MastercardCreditCardIcon/index.tsx-5-6 (1)
5-6:⚠️ Potential issue | 🟠 MajorMerge
sxas an array to handle all prop forms.
SvgIconProps["sx"]can be an object, function, or array. Spreadingsxdirectly will silently fail with non-object forms. Normalize to an array instead.Suggested fix
const MastercardCreditCardIcon: FC<SvgIconProps> = ({ sx, ...props }) => ( - <SvgIcon sx={{ fontSize: 24, color: 'action.active', ...sx }} {...props}> + <SvgIcon + sx={[ + { fontSize: 24, color: 'action.active' }, + ...(Array.isArray(sx) ? sx : sx ? [sx] : []), + ]} + {...props} + >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/design-system/components/web/icons/MastercardCreditCardIcon/index.tsx` around lines 5 - 6, The component MastercardCreditCardIcon spreads sx directly which fails for non-object forms; update the SvgIcon sx prop to normalize sx into an array by merging the default style object ({ fontSize: 24, color: 'action.active' }) with the incoming sx converted to an array (treat undefined as empty, if Array.isArray(sx) use it, else wrap sx in [sx]) so SvgIcon receives a combined array-style sx that supports object, function, or array forms; update MastercardCreditCardIcon to compute this mergedSx and pass it to <SvgIcon sx={mergedSx} ...props>.packages/design-system/components/web/icons/MastercardCreditCardIcon/index.tsx-6-31 (1)
6-31:⚠️ Potential issue | 🟠 MajorMove the
viewBoxtoSvgIconand remove the nested<svg>element.
SvgIconalready renders the outer SVG element. Wrapping SVG content in a nested<svg>with explicitwidthandheightattributes prevents MUI's sizing andfontSizeprops from controlling the artwork correctly. Per MUI's API, pass drawing elements (<rect>,<defs>, etc.) directly as children ofSvgIcon.For this non-24×24 icon (36×24), set the
viewBoxprop onSvgIconto"0 0 36 24"rather than on a nested element.Suggested fix
- <SvgIcon sx={{ fontSize: 24, color: 'action.active', ...sx }} {...props}> - <svg - width="36" - height="24" - viewBox="0 0 36 24" - fill="none" - xmlns="http://www.w3.org/2000/svg" - xmlnsXlink="http://www.w3.org/1999/xlink" - > + <SvgIcon + viewBox="0 0 36 24" + sx={{ fontSize: 24, color: 'action.active', ...sx }} + {...props} + > <rect x="5" y="3" width="26" height="18" fill="url(`#pattern0_0_8810`)" /> <defs> <pattern id="pattern0_0_8810" patternContentUnits="objectBoundingBox" width="1" height="1"> <use xlinkHref="#image0_0_8810" transform="matrix(0.00713719 0 0 0.0103093 -0.0138779 0)" /> </pattern> <image id="image0_0_8810" width="144" height="97" preserveAspectRatio="none" xlinkHref="data:image/png;base64,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" /> </defs> - </svg> </SvgIcon>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/design-system/components/web/icons/MastercardCreditCardIcon/index.tsx` around lines 6 - 31, The nested <svg> is blocking MUI SvgIcon sizing; remove the inner <svg> element and move its viewBox="0 0 36 24" up to the SvgIcon component (e.g., <SvgIcon viewBox="0 0 36 24" sx={{ fontSize: 24, ...sx }} {...props}>), then place the drawing children (rect, defs, pattern id="pattern0_0_8810", image id="image0_0_8810", etc.) directly inside SvgIcon; also remove explicit width/height attributes and unnecessary xmlns/xlink attributes from the removed <svg> so MUI fontSize and sizing control the icon correctly.packages/components/modules/payments/web/utils/stripe.ts-3-4 (1)
3-4:⚠️ Potential issue | 🟠 MajorReturn
nullinstead of a rejected promise when the Stripe key is unavailable.The
Elementscomponent from@stripe/react-stripe-jsexpects thestripeprop to be a Stripe instance,null, or a Promise that resolves to either—not a rejected promise. ReturningPromise.reject(...)violates this contract and can cause unhandled rejections since the library doesn't expect to handle promise rejections.All three call sites pass this helper directly to
Elementswithout error boundaries, so a rejected promise will surface as an unhandled rejection. Usenullinstead, which is the pattern Stripe documents for cases where initialization isn't ready.Recommended change
import { loadStripe } from '@stripe/stripe-js' -export const getStripePromise = (key: string) => - key ? loadStripe(key) : Promise.reject(new Error('Stripe publishable key is not defined')) +export const getStripePromise = (key?: string) => (key ? loadStripe(key) : null)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/utils/stripe.ts` around lines 3 - 4, getStripePromise currently returns Promise.reject(...) when key is falsy, which can cause unhandled rejections since `@stripe/react-stripe-js` expects a Stripe instance, null, or a resolving Promise; change getStripePromise (the exported function) to return null instead of a rejected Promise when the key is missing so callers (and Elements) receive null per Stripe docs; keep the successful branch using loadStripe(key) unchanged.packages/utils/constants/date.ts-7-7 (1)
7-7:⚠️ Potential issue | 🟠 MajorEscape the literal
Tin this timestamp format.This constant is used with Luxon's
toFormat()andfromFormat()methods, whereTis a recognized token. To render a literalT, wrap it in single quotes:"yyyy-MM-dd'T'HH:mm:ss.SSS".Proposed fix
- iso: 'yyyy-MM-ddTHH:mm:ss.SSS', + iso: "yyyy-MM-dd'T'HH:mm:ss.SSS",🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/utils/constants/date.ts` at line 7, The iso format string in the constants file is using an unescaped T token which Luxon treats as a format token; update the iso constant (in packages/utils/constants/date.ts, symbol: iso) to escape the literal T so Luxon prints/parses it as a character (use "yyyy-MM-dd'T'HH:mm:ss.SSS") and ensure any usages with DateTime.toFormat()/fromFormat() rely on this corrected constant.packages/components/modules/payments/web/CheckoutComponent/utils.tsx-22-24 (1)
22-24:⚠️ Potential issue | 🟠 MajorMask at least one character for short email usernames.
Line 22 currently leaves
1- to3-character usernames fully visible, so values likeab@example.comcome back unmasked. That defeats the purpose of this helper for a subset of customer emails in the checkout flow.Proposed fix
- const visibleLength = Math.min(visibleUsernameChars, username.length) + const visibleLength = + username.length <= 1 + ? 0 + : Math.min(visibleUsernameChars, username.length - 1) const maskedUsername = username.slice(0, visibleLength) + maskChar.repeat(username.length - visibleLength)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/CheckoutComponent/utils.tsx` around lines 22 - 24, The current logic lets very short usernames remain fully visible; update how visibleLength is computed so we always reserve at least one character to mask (e.g., compute visibleLength as Math.min(visibleUsernameChars, Math.max(0, username.length - 1))) and then build maskedUsername using that visibleLength with maskChar.repeat(username.length - visibleLength); change the variable visibleLength calculation (used by maskedUsername) accordingly to ensure at least one character is masked for short usernames.packages/components/modules/payments/web/InvoiceListTable/index.tsx-54-56 (1)
54-56:⚠️ Potential issue | 🟠 MajorInvalid table structure: CircularProgress directly inside TableBody.
Placing
CircularProgressdirectly inside<TableBody>produces invalid HTML table structure. It must be wrapped in<TableRow>and<TableCell>.Proposed fix
<TableBody> - {isFetching && <CircularProgress sx={{ margin: 'auto' }} />} + {isFetching && ( + <TableRow> + <TableCell colSpan={5} sx={{ textAlign: 'center' }}> + <CircularProgress /> + </TableCell> + </TableRow> + )} {isResultsEmpty ? (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/InvoiceListTable/index.tsx` around lines 54 - 56, The CircularProgress is rendered directly inside TableBody (see TableBody and isFetching) which creates invalid table markup; wrap the loader inside a TableRow and TableCell (use TableRow and TableCell components) and set the TableCell to span the full width (e.g., colSpan equal to the number of table columns or a safe value) and center the CircularProgress via sx or align properties so the loader is validly contained within the table structure.packages/components/modules/payments/web/hooks/useStripeHook.tsx-141-144 (1)
141-144:⚠️ Potential issue | 🟠 MajorSeparate the combined query key into individual invalidateQueries calls.
The queryKey format doesn't match the registered queries.
useGetCustomerregisters with a single-element key ([STRIPE_API_KEY.getCustomer(...)]), anduseGetSubscriptionregisters with a single-element key ([STRIPE_API_KEY.getSubscription(...)]). The invalidateQueries call creates a two-element key ([getCustomer(), getSubscription()]), which won't match either registered query due to React Query's exact key matching.Replace with separate invalidateQueries calls for each query:
queryClient.invalidateQueries({ queryKey: [STRIPE_API_KEY.getCustomer()], }) queryClient.invalidateQueries({ queryKey: [STRIPE_API_KEY.getSubscription()], })Also applies to: 167-169
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/hooks/useStripeHook.tsx` around lines 141 - 144, The current invalidateQueries call passes a combined two-element key which doesn't match the single-element keys registered by useGetCustomer and useGetSubscription; replace the single call with two calls to queryClient.invalidateQueries, one using queryKey: [STRIPE_API_KEY.getCustomer()] and another using queryKey: [STRIPE_API_KEY.getSubscription()], and apply the same change to the other instance noted around the block referencing queryClient.invalidateQueries at 167-169 so both useGetCustomer and useGetSubscription are invalidated individually.packages/components/modules/payments/web/PaymentMethodsManagementComponent/PaymentMethodsItem/index.tsx-39-48 (1)
39-48:⚠️ Potential issue | 🟠 MajorUse the clicked button as the menu anchor.
document.activeElementis not guaranteed to be thisIconButton, so the menu can anchor to the wrong element after mouse/touch interactions. Capture the click event and useevent.currentTarget; while touching this, please add anaria-labelto the icon-only button.💡 Suggested fix
<IconButton - onClick={() => { + aria-label={`Actions for card ending in ${paymentMethod?.card?.last4 ?? ''}`} + onClick={(event) => { setIsMenuOpen(true) setSelectedPaymentMethodId(paymentMethod.id) - setAnchorEl(document.activeElement as HTMLElement) + setAnchorEl(event.currentTarget) }} sx={{ flexGrow: 0 }} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/PaymentMethodsManagementComponent/PaymentMethodsItem/index.tsx` around lines 39 - 48, The IconButton click handler should use the clicked element as the menu anchor and include an accessible label: change the onClick to accept the click event and call setIsMenuOpen(true), setSelectedPaymentMethodId(paymentMethod.id), and setAnchorEl(event.currentTarget as HTMLElement) instead of document.activeElement; also add an aria-label (e.g. "more options") to the IconButton so the icon-only button is accessible. Target identifiers: IconButton, onClick handler, setIsMenuOpen, setSelectedPaymentMethodId, setAnchorEl, and paymentMethod.id.packages/components/modules/payments/web/hooks/useStripeHook.tsx-191-194 (1)
191-194:⚠️ Potential issue | 🟠 MajorAdd
entityIdto the invoice list key.The query function uses both
pageandentityIdto fetch data, but the cache key only includespage. This causes cache collisions where requests from different users/tenants with the same page number receive the cached result from another user's request, exposing incorrect data.The queryKey should be:
queryKey: [STRIPE_API_KEY.listInvoices(page.toString(), entityId)]or similar, to ensure the cache key varies by both parameters.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/hooks/useStripeHook.tsx` around lines 191 - 194, The cache key for useListInvoices doesn't include entityId, causing collisions; update useListInvoices so the queryKey includes both page and entityId (e.g., use STRIPE_API_KEY.listInvoices(page.toString(), entityId) or otherwise incorporate entityId) to match the parameters passed to StripeApi.listInvoices and ensure per-entity caching.packages/components/modules/payments/web/AvailableSubscriptions/index.tsx-15-16 (1)
15-16:⚠️ Potential issue | 🟠 MajorAdd null guard to
onChangeand initializeselectedTermbased on available products.Line 40:
setSelectedTerm(value)can receivenullfrom the exclusive toggle (when re-clicking the selected button), but the type signature requires'monthly' | 'yearly'. Additionally, defaulting to'monthly'on line 15 causes the first render to show no selected button and no cards if only yearly products exist.Also, lines 47 and 50 have redundant guards:
monthlySubs?.length &&already evaluates to falsy when the length is0, so the additional> 0check is unnecessary.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/AvailableSubscriptions/index.tsx` around lines 15 - 16, The onChange handler for the exclusive toggle should guard against null before calling setSelectedTerm (only call setSelectedTerm when value !== null) and the selectedTerm state should be initialized based on available products (e.g., set initial state to 'yearly' if yearlySubs exist, otherwise 'monthly') so the first render selects an available term; also remove the redundant length checks like monthlySubs?.length && monthlySubs.length > 0 (keep only monthlySubs?.length or monthlySubs && monthlySubs.length) to simplify the guards.packages/components/modules/payments/web/PaymentDropDown/index.tsx-31-34 (1)
31-34:⚠️ Potential issue | 🟠 MajorReplace the
@ts-ignorewith a proper fix: create a new Elements instance instead of updating clientSecret.
elements.update()does not support changingclientSecretafter initialization. TheStripeElementsUpdateOptionstype omitsclientSecretentirely—it is only available during Elements construction viastripe.elements({ clientSecret }).Calling
elements.update({ clientSecret })will silently fail (Stripe ignores the change), leaving the modal with stale Elements state during payment setup. This breaks the payment flow.Create a new Elements instance with the new secret instead, either by remounting the
<Elements>component with a differentkeyor conditionally rendering it once the secret is available.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/PaymentDropDown/index.tsx` around lines 31 - 34, The onSuccess handler currently calls elements?.update({ clientSecret }) with a ts-ignore, but Stripe Elements cannot change clientSecret after initialization; remove the elements.update call and instead create a fresh Elements instance when the new SetupIntent arrives: store setupIntent.clientSecret in state (e.g., paymentSetupClientSecret) in the onSuccess callback alongside setIsAddCardModalOpen, and drive the <Elements> mounting by that secret (either render <Elements stripe={stripe} options={{clientSecret: paymentSetupClientSecret}}> only when the secret exists or change the Elements component key to force remount); update references to elements and the onSuccess function to rely on the newly constructed Elements rather than elements.update.packages/components/modules/payments/web/SubscriptionManagement/index.tsx-60-77 (1)
60-77:⚠️ Potential issue | 🟠 MajorWait for the customer query before falling back to the free-plan UI.
hasSubscriptionis derived fromcustomer, butisLoadingignoresuseGetCustomer. A slow customer fetch can renderFreePlanComponentfor subscribed accounts.🩹 Proposed fix
- const { data: customer, refetch: refetchCustomer } = useGetCustomer(entityId) + const { + data: customer, + refetch: refetchCustomer, + isLoading: isLoadingCustomer, + } = useGetCustomer(entityId) @@ - const isLoading = isLoadingMethods || isLoadingSubscription + const isLoading = isLoadingCustomer || isLoadingMethods || isLoadingSubscriptionAlso applies to: 149-151
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/SubscriptionManagement/index.tsx` around lines 60 - 77, The UI falls back to FreePlanComponent before the customer query finishes because isLoading omits the useGetCustomer state; update the loading logic to include the customer fetch and ensure any guard that derives hasSubscription from customer waits for customer to be defined before rendering the free-plan UI. Specifically, modify the isLoading calculation (used alongside useGetCustomer, useGetSubscription, useListPaymentMethods) to include the customer loading flag from useGetCustomer and ensure the component logic that checks hasSubscription (and the FreePlanComponent fallback) only runs once customer is loaded; apply the same change where hasSubscription is used around the other related block (around the code referenced at lines 149-151).packages/components/modules/payments/web/CheckoutComponent/index.tsx-219-223 (1)
219-223:⚠️ Potential issue | 🟠 MajorDon't store
'empty'as a real payment-method id.When there is no default card, this seeds
selectedPaymentMethodIdwith'empty', and the submit path will send that literal string unless the user changes the dropdown.🩹 Proposed fix
useEffect(() => { if (!paymentMethods || paymentMethods.length === 0) return const defaultPaymentMethod = paymentMethods?.find((pm) => pm.isDefault) - setSelectedPaymentMethodId(defaultPaymentMethod?.id ?? 'empty') + setSelectedPaymentMethodId(defaultPaymentMethod?.id ?? paymentMethods[0]?.id ?? '') }, [paymentMethods])🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/CheckoutComponent/index.tsx` around lines 219 - 223, The useEffect seeds selectedPaymentMethodId with the literal string 'empty' which can be submitted as a real id; change the logic in the useEffect that reads paymentMethods and sets selection (the block using paymentMethods, defaultPaymentMethod, and setSelectedPaymentMethodId) to set a null/undefined (or empty string) sentinel instead of the literal 'empty' when no default exists, and update the submit path that reads selectedPaymentMethodId (form submit handler) to treat null/undefined/empty-string as "no payment method selected" and reject/handle accordingly so the string 'empty' is never sent to the API.packages/components/modules/payments/web/PaymentMethodsManagementComponent/index.tsx-71-96 (1)
71-96:⚠️ Potential issue | 🟠 MajorSetting/removing a card should not depend on Elements initialization.
These handlers only hit your backend mutations. Guarding them with
!elementsmakes existing payment-method management fail whenever Stripe.js is slow or blocked.🩹 Proposed fix
const handleUpdatePaymentMethod = async () => { - if (!elements) { - console.error('Stripe elements not initialized') + if (!selectedPaymentMethodId) { return } @@ const handleDeletePaymentMethod = async () => { - if (!elements) { - console.error('Stripe elements not initialized') + if (!selectedPaymentMethodId) { return }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/PaymentMethodsManagementComponent/index.tsx` around lines 71 - 96, The handlers handleUpdatePaymentMethod and handleDeletePaymentMethod incorrectly bail out when the Stripe `elements` object is not initialized; remove the `if (!elements) { ... return }` guards so these functions always call the backend mutations `updatePaymentMethod` and `deletePaymentMethod` (and still perform post-mutation state updates like setSelectedPaymentMethodId and setIsConfirmationDialogOpen in handleDeletePaymentMethod), ensuring payment-method management does not depend on Stripe Elements initialization.packages/components/modules/payments/web/PaymentMethodsManagementComponent/index.tsx-1-1 (1)
1-1:⚠️ Potential issue | 🟠 MajorAdd
'use client'before Line 1.This exported entrypoint uses React hooks (
useState,useEffect) and Stripe hooks (useElements,useStripe), requiring a client boundary. Other payment screens in the same directory (SubscriptionManagement, AvailableSubscriptions, CheckoutComponent) all declare'use client'. Without it, importing this component from an App Router server component through the public barrel export will break.🩹 Proposed fix
+'use client' + import { FC, useEffect, useState } from 'react'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/PaymentMethodsManagementComponent/index.tsx` at line 1, This file exports a component (PaymentMethodsManagementComponent) that uses React hooks (useState, useEffect) and Stripe hooks (useElements, useStripe) but lacks a client boundary; add the module directive 'use client' at the very top of the file (before the existing imports) so this component is treated as a client component and can be safely imported from App Router server components.packages/components/modules/payments/web/CheckoutComponent/index.tsx-173-178 (1)
173-178:⚠️ Potential issue | 🟠 MajorInvalidate the customer and payment-method caches with separate calls.
The current code wraps two different query keys in a single array, but React Query's prefix matching won't find either cache entry. Query keys are registered as
[['stripe', 'listPaymentMethods', entityId]]and[['stripe', 'getCustomer', entityId]]respectively, so passing both as a single filter[[key1], [key2]]fails to match either one.Proposed fix
- queryClient.invalidateQueries({ - queryKey: [ - STRIPE_API_KEY.listPaymentMethods(entityId), - STRIPE_API_KEY.getCustomer(entityId), - ], - }) + queryClient.invalidateQueries({ + queryKey: STRIPE_API_KEY.listPaymentMethods(entityId), + }) + queryClient.invalidateQueries({ + queryKey: STRIPE_API_KEY.getCustomer(entityId), + })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/CheckoutComponent/index.tsx` around lines 173 - 178, The invalidate logic is passing two distinct query keys inside a single array to queryClient.invalidateQueries which won't match registered keys; call queryClient.invalidateQueries separately for each key (use STRIPE_API_KEY.listPaymentMethods(entityId) and STRIPE_API_KEY.getCustomer(entityId) in two invalidateQueries calls) so each registered query key is matched and invalidated correctly.packages/components/modules/payments/web/SubscriptionManagement/index.tsx-137-141 (1)
137-141:⚠️ Potential issue | 🟠 MajorRemove the immediate
refetchSubscription()call from this effect.
setSubscriptionId()is asynchronous, sorefetchSubscription()executes with the stale (empty)subscriptionId. The query will automatically refetch when the state updates and the newsubscriptionIdbecomes non-empty, since the hook hasenabled: !!subscriptionId.🩹 Proposed fix
useEffect(() => { if (customer?.subscriptions?.[0]?.id) { setSubscriptionId(customer.subscriptions[0].id) - refetchSubscription() } }, [customer])🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/SubscriptionManagement/index.tsx` around lines 137 - 141, The effect that checks customer.subscriptions should not call refetchSubscription() immediately because setSubscriptionId is async and the query is already configured with enabled: !!subscriptionId; remove the explicit refetchSubscription() call from the useEffect that contains setSubscriptionId (the effect referencing customer?.subscriptions?.[0]?.id) so the subscription query will refetch naturally when subscriptionId state updates.packages/components/modules/payments/web/PaymentMethodsManagementComponent/index.tsx-98-105 (1)
98-105:⚠️ Potential issue | 🟠 MajorInclude
elementsin the setup-intent effect dependencies.If the setup intent resolves before
useElements()does, the optional chaining prevents the elements from being updated with the clientSecret, and the effect will never rerun sinceelementsis not listed as a dependency.🩹 Proposed fix
useEffect(() => { - if (isCreatingSetupIntent || isErrorCreatingSetupIntent) return - if (setupIntent) { - // `@ts-ignore` - elements?.update({ clientSecret: setupIntent.clientSecret }) - setIsAddCardModalOpen(true) - } - }, [setupIntent, isCreatingSetupIntent, isErrorCreatingSetupIntent]) + if (!elements || isCreatingSetupIntent || isErrorCreatingSetupIntent || !setupIntent) return + // `@ts-ignore` + elements.update({ clientSecret: setupIntent.clientSecret }) + setIsAddCardModalOpen(true) + }, [elements, setupIntent, isCreatingSetupIntent, isErrorCreatingSetupIntent])🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/PaymentMethodsManagementComponent/index.tsx` around lines 98 - 105, The effect in PaymentMethodsManagementComponent that updates Stripe Elements when a setupIntent arrives doesn't list elements (from useElements()) in its dependency array, so if setupIntent resolves before elements the elements.update call is skipped and the effect won't rerun; update the useEffect to include elements in the dependency array and ensure you guard for null (e.g., return early if isCreatingSetupIntent/isErrorCreatingSetupIntent or if elements is falsy) so that when elements becomes available the effect runs, performs elements.update({ clientSecret: setupIntent.clientSecret }) and then calls setIsAddCardModalOpen(true).packages/components/modules/payments/web/AddCardModal/index.tsx-52-54 (1)
52-54:⚠️ Potential issue | 🟠 MajorPass the
entityIdto ensure query invalidation matches the registered query key.
STRIPE_API_KEY.listPaymentMethods()is called without theentityIdparameter, but the query is registered with it. SinceinvalidateQueriesuses prefix matching on the flattened key, wrapping the result in brackets creates a structure that won't match the registered query pattern. PassentityIdand use the flat key.🐛 Proposed fix
await queryClient.invalidateQueries({ - queryKey: [STRIPE_API_KEY.listPaymentMethods()], + queryKey: STRIPE_API_KEY.listPaymentMethods(entityId), })🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/AddCardModal/index.tsx` around lines 52 - 54, The invalidateQueries call uses a wrapped array and omits entityId so it doesn't match the registered query key; update the call to pass the entityId into STRIPE_API_KEY.listPaymentMethods(entityId) and supply that flat key directly to queryClient.invalidateQueries (i.e., use queryClient.invalidateQueries({ queryKey: STRIPE_API_KEY.listPaymentMethods(entityId) }) or the equivalent flat key form) so the invalidation matches the registered query; adjust the code around STRIPE_API_KEY.listPaymentMethods and queryClient.invalidateQueries to use the entityId variable.
🟡 Minor comments (7)
packages/design-system/components/web/icons/MastercardCreditCardIcon/index.tsx-15-28 (1)
15-28:⚠️ Potential issue | 🟡 MinorNamespace the SVG
defsIDs per instance.These hardcoded IDs will repeat every time the icon is rendered. In views that show multiple cards at once, duplicate
ids make the DOM invalid and can cross-wire theurl(#...)/xlinkHrefreferences.Suggested fix
-import { FC } from 'react' +import { FC, useId } from 'react' -const MastercardCreditCardIcon: FC<SvgIconProps> = ({ sx, ...props }) => ( - <SvgIcon ...> - <rect x="5" y="3" width="26" height="18" fill="url(`#pattern0_0_8810`)" /> +const MastercardCreditCardIcon: FC<SvgIconProps> = ({ sx, ...props }) => { + const patternId = useId() + const imageId = useId() + + return ( + <SvgIcon ...> + <rect x="5" y="3" width="26" height="18" fill={`url(#${patternId})`} /> <defs> - <pattern id="pattern0_0_8810" patternContentUnits="objectBoundingBox" width="1" height="1"> + <pattern id={patternId} patternContentUnits="objectBoundingBox" width="1" height="1"> <use - xlinkHref="#image0_0_8810" + xlinkHref={`#${imageId}`} transform="matrix(0.00713719 0 0 0.0103093 -0.0138779 0)" /> </pattern> <image - id="image0_0_8810" + id={imageId} width="144" height="97" preserveAspectRatio="none" xlinkHref="data:image/png;base64,..." /> </defs> - </SvgIcon> -) + </SvgIcon> + ) +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/design-system/components/web/icons/MastercardCreditCardIcon/index.tsx` around lines 15 - 28, The SVG defs IDs (pattern0_0_8810, image0_0_8810) are static and will collide when multiple MastercardCreditCardIcon instances render; update the component to generate a unique suffix (e.g., via React's useId() or an optional id prop) and append it to the ids in the <defs> (pattern and image) and to every reference (rect fill="url(#...)" and <use xlinkHref="#..."> and image xlinkHref) so each instance has namespaced IDs (update the MastercardCreditCardIcon component where pattern0_0_8810 and image0_0_8810 are declared and referenced).packages/components/modules/payments/web/InvoiceListTable/InvoiceListTableHeader/index.tsx-6-16 (1)
6-16:⚠️ Potential issue | 🟡 MinorColumn count mismatch between mobile header and footer.
The mobile header renders 3 columns (Description, Info, empty), but the
InvoiceListTableFootercomponent uses a hardcodedcolSpan={5}. This mismatch may cause layout issues on mobile views where the footer cell spans more columns than exist.Consider making
colSpandynamic based on thesmDownprop to ensure proper alignment.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/InvoiceListTable/InvoiceListTableHeader/index.tsx` around lines 6 - 16, The mobile header renders only 3 columns when smDown is true, but InvoiceListTableFooter uses a hardcoded colSpan={5}; update the footer to compute its colspan from the same responsive flag instead of hardcoding: e.g., derive a columnsCount = smDown ? 3 : 5 (or compute from the header structure) and use that for the InvoiceListTableFooter colSpan prop; ensure the responsive prop (smDown) is passed into or read by InvoiceListTableFooter so TableHead and footer stay in sync.packages/components/modules/payments/web/InvoiceListTable/InvoiceListTableFooter/index.tsx-16-16 (1)
16-16:⚠️ Potential issue | 🟡 MinorHardcoded
colSpan={5}may cause layout issues on mobile.As noted in the header component review, the mobile view uses 3 columns while this footer spans 5. Consider receiving
colSpanas a prop or deriving it fromsmDownto maintain consistency.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/InvoiceListTable/InvoiceListTableFooter/index.tsx` at line 16, The TableCell in InvoiceListTableFooter currently hardcodes colSpan={5}, which breaks the mobile layout; update the InvoiceListTableFooter component to accept an optional prop (e.g., colSpan?: number) and/or compute colSpan based on the smDown media query (useTheme/useMediaQuery) so it uses 3 when smDown is true and 5 otherwise, and ensure any passed prop overrides the computed value; change the component signature and the TableCell usage to use the derivedOrPropColSpan to keep header/footer column counts consistent.packages/design-system/components/web/icons/VisaCreditCardIcon/index.tsx-15-26 (1)
15-26:⚠️ Potential issue | 🟡 MinorSVG ID collision risk if same icon renders multiple times on a page.
The hardcoded IDs (
pattern0_0_8928,image0_0_8928) are document-global. When multipleVisaCreditCardIconinstances render on the same page, they share the same ID references in the DOM, which could cause visual issues if the second instance's<defs>overwrites the first.While the current usage pattern (conditional rendering per card type) makes this unlikely, consider using unique IDs via React 18's
useId()hook or inlining the fill to eliminate the dependency on pattern references.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/design-system/components/web/icons/VisaCreditCardIcon/index.tsx` around lines 15 - 26, The SVG uses hardcoded IDs pattern0_0_8928 and image0_0_8928 inside VisaCreditCardIcon which can collide when multiple instances render; fix by generating unique IDs (e.g., use React's useId() inside VisaCreditCardIcon) and replace the static IDs in <pattern id="..."> and <image id="..."> as well as the references (use xlinkHref="#image..." and rect fill="url(`#pattern`...)") with the generated unique values; ensure you import useId from React and wire the unique id into all three places (pattern id, image id, and the url/# references).packages/components/modules/payments/web/SubscriptionManagement/styled.tsx-16-23 (1)
16-23:⚠️ Potential issue | 🟡 MinorReplace
paddingYwith explicit top/bottom padding.
paddingYis a MUI System prop supported only insxor system style functions. Insidestyled()callback objects, only plain CSS properties are recognized, so this shorthand is ignored and the vertical spacing is lost. UsepaddingTopandpaddingBottominstead.Suggested fix
export const PaymentMethodContainer = styled(Box)(({ theme }) => ({ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', alignSelf: 'stretch', gap: theme.spacing(4), - paddingY: theme.spacing(4), + paddingTop: theme.spacing(4), + paddingBottom: theme.spacing(4), flex: 1, }))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/SubscriptionManagement/styled.tsx` around lines 16 - 23, The styled callback for PaymentMethodContainer (styled(Box)) uses the unsupported shorthand paddingY which is ignored; replace it with explicit paddingTop and paddingBottom using the same value (theme.spacing(4)) so the vertical spacing is applied (i.e., remove paddingY and add paddingTop: theme.spacing(4), paddingBottom: theme.spacing(4) inside the styled object).packages/components/modules/payments/web/InvoiceListTable/InvoiceItemWrapper/index.tsx-9-9 (1)
9-9:⚠️ Potential issue | 🟡 MinorPotential NaN and unnecessary nullish coalescing.
If
row.amountDueisundefinedornull, the division will produceNaN, andtoFixed(2)will return"NaN". The?? ''is also ineffective sincetoFixed()always returns a string.Proposed fix
- const amountDue = (row.amountDue / 100).toFixed(2) ?? '' + const amountDue = row.amountDue != null ? (row.amountDue / 100).toFixed(2) : ''🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/InvoiceListTable/InvoiceItemWrapper/index.tsx` at line 9, The amountDue computation can produce "NaN" because row.amountDue may be null/undefined and the ?? '' after toFixed() is ineffective; update the amountDue calculation (the const amountDue variable) to first verify row.amountDue is a finite number (e.g., using Number.isFinite(row.amountDue) or typeof/check !== null) and only then compute (row.amountDue / 100).toFixed(2); otherwise set amountDue to an empty string (''), and remove the unnecessary nullish coalescing.packages/components/modules/payments/web/AvailableSubscriptions/index.tsx-47-52 (1)
47-52:⚠️ Potential issue | 🟡 MinorThese conditions can render a literal
0.Use a single boolean guard instead of chaining
length && length > 0.💡 Suggested fix
- {monthlySubs?.length && monthlySubs?.length > 0 && ( + {(monthlySubs?.length ?? 0) > 0 && ( <ToggleButton value="monthly">Monthly</ToggleButton> )} - {yearlySubs?.length && yearlySubs?.length > 0 && ( + {(yearlySubs?.length ?? 0) > 0 && ( <ToggleButton value="yearly">Yearly</ToggleButton> )}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/payments/web/AvailableSubscriptions/index.tsx` around lines 47 - 52, The conditional checks for rendering ToggleButton currently use "monthlySubs?.length && monthlySubs?.length > 0" (and similarly for yearlySubs) which can render a literal 0; replace each chain with a single boolean guard such as "monthlySubs?.length > 0" (or "Boolean(monthlySubs?.length)") and similarly "yearlySubs?.length > 0" to avoid rendering 0 and simplify the logic around the ToggleButton components.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e1605fc4-5bb9-483a-a70a-93764d6b541e
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (50)
packages/components/modules/payments/web/AddCardModal/index.tsxpackages/components/modules/payments/web/AddCardModal/styled.tsxpackages/components/modules/payments/web/AddCardModal/types.tspackages/components/modules/payments/web/AvailableSubscriptions/SubscriptionCard/index.tsxpackages/components/modules/payments/web/AvailableSubscriptions/index.tsxpackages/components/modules/payments/web/AvailableSubscriptions/styled.tsxpackages/components/modules/payments/web/AvailableSubscriptions/types.tspackages/components/modules/payments/web/CheckoutComponent/ConfirmationSubscriptionModal/index.tsxpackages/components/modules/payments/web/CheckoutComponent/ConfirmationSubscriptionModal/styled.tsxpackages/components/modules/payments/web/CheckoutComponent/ConfirmationSubscriptionModal/types.tspackages/components/modules/payments/web/CheckoutComponent/index.tsxpackages/components/modules/payments/web/CheckoutComponent/styled.tsxpackages/components/modules/payments/web/CheckoutComponent/types.tspackages/components/modules/payments/web/CheckoutComponent/utils.tsxpackages/components/modules/payments/web/InvoiceListTable/InvoiceItemTableRow/index.tsxpackages/components/modules/payments/web/InvoiceListTable/InvoiceItemWrapper/index.tsxpackages/components/modules/payments/web/InvoiceListTable/InvoiceListTableFooter/index.tsxpackages/components/modules/payments/web/InvoiceListTable/InvoiceListTableHeader/index.tsxpackages/components/modules/payments/web/InvoiceListTable/MobileInvoiceItemTableRow/index.tsxpackages/components/modules/payments/web/InvoiceListTable/index.tsxpackages/components/modules/payments/web/InvoiceListTable/types.tspackages/components/modules/payments/web/PaymentDropDown/AddPaymentMethodItem/index.tsxpackages/components/modules/payments/web/PaymentDropDown/PaymentMethodDisplay/index.tsxpackages/components/modules/payments/web/PaymentDropDown/index.tsxpackages/components/modules/payments/web/PaymentDropDown/styled.tsxpackages/components/modules/payments/web/PaymentDropDown/types.tspackages/components/modules/payments/web/PaymentMethodsManagementComponent/PaymentMethodsItem/index.tsxpackages/components/modules/payments/web/PaymentMethodsManagementComponent/index.tsxpackages/components/modules/payments/web/PaymentMethodsManagementComponent/types.tsxpackages/components/modules/payments/web/SubscriptionManagement/CancelSubscriptionModal/index.tsxpackages/components/modules/payments/web/SubscriptionManagement/FreePlanComponent/index.tsxpackages/components/modules/payments/web/SubscriptionManagement/index.tsxpackages/components/modules/payments/web/SubscriptionManagement/styled.tsxpackages/components/modules/payments/web/SubscriptionManagement/types.tspackages/components/modules/payments/web/SubscriptionManagement/utils.tspackages/components/modules/payments/web/hooks/useStripeHook.tsxpackages/components/modules/payments/web/index.tspackages/components/modules/payments/web/services/stripe.tspackages/components/modules/payments/web/types.tspackages/components/modules/payments/web/utils/index.tsxpackages/components/modules/payments/web/utils/stripe.tspackages/components/package.jsonpackages/design-system/components/web/dialogs/ConfirmDialog/index.tsxpackages/design-system/components/web/dialogs/ConfirmDialog/types.tspackages/design-system/components/web/icons/CheckIcon/index.tsxpackages/design-system/components/web/icons/CreditCardIcon/index.tsxpackages/design-system/components/web/icons/MastercardCreditCardIcon/index.tsxpackages/design-system/components/web/icons/VisaCreditCardIcon/index.tsxpackages/design-system/components/web/icons/index.tspackages/utils/constants/date.ts
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/components/package.json`:
- Around line 102-103: The package.json exports include a broken mapping
"./pages/web" -> "./modules/pages/web/index.ts" which doesn't exist; either
remove that export entry or add the missing module. Fix by editing the exports
object in package.json to delete the "./pages/web" export line (leaving
"./pages/common") or create the directory modules/pages/web with an index.ts
that re-exports the intended symbols (matching the structure used by
"./modules/pages/common/index.ts") so imports of
`@baseapp-frontend/components/pages/web` resolve correctly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: a7695c79-0d37-42c0-87d8-7b6174b34d2e
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (2)
packages/components/package.jsonpackages/design-system/components/web/icons/index.ts
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
packages/components/modules/profiles/common/types.ts (1)
4-4: Consider type-only imports for generated GraphQL types to clarify intent.While the current regular imports work safely (TypeScript's
verbatimModuleSyntax: falseautomatically elides unused imports), type-only imports make it explicit that these are compile-time dependencies. However, note that this codebase uses regular imports for generated GraphQL types as the dominant convention; only a small number of files useimport type. Align this change with your team's style preference before applying.♻️ Proposed cleanup
-import { AllProfilesListFragment$data } from '../../../__generated__/AllProfilesListFragment.graphql' -import { ProfileComponentFragment$data } from '../../../__generated__/ProfileComponentFragment.graphql' +import type { AllProfilesListFragment$data } from '../../../__generated__/AllProfilesListFragment.graphql' +import type { ProfileComponentFragment$data } from '../../../__generated__/ProfileComponentFragment.graphql' export type { ProfileComponentFragment$data }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/components/modules/profiles/common/types.ts` at line 4, The file currently uses a regular import/export for generated GraphQL types; change any non-type imports of the generated fragment to explicit type-only imports/exports so intent is clear — e.g., use "import type { ProfileComponentFragment$data }" (or ensure the re-export stays as "export type { ProfileComponentFragment$data }") and update any other generated GraphQL symbols similarly (search for ProfileComponentFragment$data and other GraphQL-generated names) to use "import type" / "export type".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/components/modules/payments/web/InvoiceListTable/index.tsx`:
- Around line 37-40: The code treats missing API responses as an empty list by
using data?.results ?? [], which hides request failures; update InvoiceListTable
to treat undefined data differently by checking the error state from
useListInvoices (e.g., isError / error) and only derive receipts from
data.results when data is defined, rendering a distinct error message/UI when
isError is true (instead of showing "No receipts found"), keep existing loading
logic using isLoading/isFetching, and apply the same change to the other
occurrences referenced around the receipts/isResultsEmpty logic (lines using
receipts, isResultsEmpty, and any conditional rendering at 48-50 and 56-63) so
failed fetches surface an explicit error state rather than an empty history.
- Line 1: When the parent swaps entityId, reset the pagination state so old page
numbers aren't reused; inside the InvoiceListTable component add an effect that
watches entityId and calls setPage(initialPage) (e.g., setPage(1) or whatever
the component's initial page value is) to reset pagination. Locate the useState
hook for page/currentPage and add a useEffect(() => { setPage(initialPage) },
[entityId]) so changing entityId always resets the page and avoids requesting
non-existent pages.
- Around line 53-58: The spinner and empty fallback must render as table rows
and use the header's column count: wrap the isFetching spinner in a <TableRow>
with a <TableCell colSpan={colSpan}> instead of rendering CircularProgress
directly inside TableBody, and replace the hard-coded colSpan={5} on the empty
fallback row with a colSpan value derived from the responsive header props (use
headerProps to compute colSpan, e.g. headerProps.colSpan ||
headerProps.columnCount || headerProps.columns?.length, defaulting only as a
last resort), keeping InvoiceListTableHeader, isFetching, isResultsEmpty and
rowProps/ headerProps references to locate the change.
- Line 41: The wrapper currently strips or mis-signatures the footer onChange:
keep and forward the original onChange signature and pass the pageNumber when
invoking it; specifically, when destructuring footerProps (const { onChange,
...restFooterProps } = footerProps ?? {}), treat onChange as a callback that
accepts the original event plus the page number and invoke it as
onChange?.(event, pageNumber) instead of calling it without the page number or
casting the event unsafely; to satisfy TS, either augment the footerProps type
locally (e.g., declare onChange?: (event: ChangeEvent<HTMLTableSectionElement>,
pageNumber: number) => void) or narrow/footerProps to a union that includes that
signature so you don’t need the unsafe cast, and ensure the invocation occurs
where the TableFooter event is handled (replace the current casted call) so the
consumer receives (event, pageNumber) intact.
In `@packages/components/package.json`:
- Around line 125-126: The package.json dependency bump risks breaking imports
for PaymentElement and AddressElement; either keep "@stripe/react-stripe-js":
"^3.6.0" or, if you want to upgrade to v5+, update all imports of PaymentElement
and AddressElement in components modules to the checkout entrypoint: change
imports in AddCardModal
(packages/components/modules/payments/web/AddCardModal/index.tsx) and
CheckoutComponent
(packages/components/modules/payments/web/CheckoutComponent/index.tsx) to import
from "@stripe/react-stripe-js/checkout", then update package.json versions for
"@stripe/react-stripe-js" and "@stripe/stripe-js" accordingly.
---
Nitpick comments:
In `@packages/components/modules/profiles/common/types.ts`:
- Line 4: The file currently uses a regular import/export for generated GraphQL
types; change any non-type imports of the generated fragment to explicit
type-only imports/exports so intent is clear — e.g., use "import type {
ProfileComponentFragment$data }" (or ensure the re-export stays as "export type
{ ProfileComponentFragment$data }") and update any other generated GraphQL
symbols similarly (search for ProfileComponentFragment$data and other
GraphQL-generated names) to use "import type" / "export type".
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1904fb44-4c01-4564-b16f-5d7edff63c21
📒 Files selected for processing (5)
packages/components/modules/messages/common/graphql/queries/GroupDetailsQuery.tspackages/components/modules/payments/README.mdpackages/components/modules/payments/web/InvoiceListTable/index.tsxpackages/components/modules/profiles/common/types.tspackages/components/package.json
💤 Files with no reviewable changes (1)
- packages/components/modules/messages/common/graphql/queries/GroupDetailsQuery.ts
✅ Files skipped from review due to trivial changes (1)
- packages/components/modules/payments/README.md
|
|
Can you please fix the 3 failed checks? |
|



__package_name__package update -v __package_version__Summary by CodeRabbit
New Features
UI polish
Documentation