Skip to content

feat(frontend): settings v2 billing page (subscription + automation credits) - #12942

Merged
Abhi1992002 merged 27 commits into
devfrom
abhimanyuyadav/billing-page-for-settings-page
Apr 30, 2026
Merged

feat(frontend): settings v2 billing page (subscription + automation credits)#12942
Abhi1992002 merged 27 commits into
devfrom
abhimanyuyadav/billing-page-for-settings-page

Conversation

@Abhi1992002

@Abhi1992002 Abhi1992002 commented Apr 29, 2026

Copy link
Copy Markdown
Member

Why / What / How

Why: The Settings V2 layout (#12885) shipped a /settings/billing route with only a "Coming soon" placeholder. This PR replaces it with the real billing experience so users can manage their subscription, payment method, invoices, automation credits, and auto top-up from the new layout.

What: A new billing page split into two tabs:

  • Subscription — current plan, payment method, recent Stripe invoices, autopilot usage.
  • Automation Credits — current balance, auto top-up config, monthly usage, and transaction history.

How:

  • Page uses the design system TabsLine molecule. Each tab is a thin orchestrator that composes per-feature cards.
  • Cards follow the project convention: ComponentName/ComponentName.tsx for render + useComponentName.ts for logic. Shared formatters live in billing/helpers.ts.
  • Backend exposes a new GET /credits/invoices endpoint backed by stripe.Invoice.list, returning the subset the UI needs (id, number, amount, status, hosted URL, PDF URL). DisabledUserCredit returns [] so the page degrades gracefully when the credit system is off.
  • openapi.json is regenerated to include the new operation; the InvoicesCard hook will switch from the credit-history fallback to the generated useGetV1ListInvoices once pnpm generate:api runs against this branch (see useInvoicesCard.next.ts for the swap).
  • TabsLineList gains an optional flush prop so the first tab aligns with the page heading without a stray indent.
Screenshot 2026-04-29 at 6 11 06 PM

Changes 🏗️

Backend (backend/)

  • data/credit.pyInvoiceListItem model + abstract list_invoices (default []) on UserCreditBase; concrete UserCredit.list_invoices calls Stripe via run_in_threadpool and bounds limit to [1, 100].
  • api/features/v1.pyGET /credits/invoices?limit=24 (auth required), returns list[InvoiceListItem].

Frontend (frontend/src/app/(platform)/settings/billing/)

  • page.tsx — replaces the placeholder with TabsLine (Subscription / Automation Credits) and sets document.title.
  • helpers.tsformatCents, formatShortDate, formatRelativeReset, EASE_OUT.
  • components/SubscriptionTab/YourPlanCard, PaymentMethodCard, InvoicesCard, AutopilotUsageCard.
  • components/AutomationCreditsTab/BalanceCard, AutoRefillCard (+ AutoRefillDialog), UsageCard, TransactionHistoryCard.

Frontend (shared)

  • openapi.json — regenerated (adds /credits/invoices).
  • components/molecules/TabsLine/TabsLine.tsx — adds flush prop on TabsLineList to drop the first trigger's left padding.
  • settings/__tests__/placeholder-pages.test.tsx — Billing now asserts the real heading + tab triggers; OAuth Apps keeps the "coming soon" assertion.

Checklist 📋

For code changes:

  • I have clearly listed my changes in the PR description
  • I have made a test plan
  • I have tested my changes according to the test plan:
    • Sign in to a Stripe-enabled account, navigate to Settings → Billing, confirm the page renders without a "Coming soon" placeholder.
    • Subscription tab: plan, payment method, invoices, and autopilot usage cards render. Invoices show a non-empty list for an account with Stripe history; "View" / "PDF" links open the Stripe-hosted invoice and the PDF.
    • Automation Credits tab: balance card shows current balance, "Auto Top-Up" opens the dialog and saves config, usage card shows the current period, transaction history paginates.
    • Tab switching preserves URL state and the heading stays aligned (no extra left indent on the first tab).
    • Disabled-credits environment: page renders with empty invoices / zero balance instead of crashing.
    • Backend: pytest backend/api/features/test_v1.py -k invoices (or an equivalent smoke test) returns a 200 with [] for a user without a Stripe customer.
    • Frontend: pnpm test:unit -- placeholder-pages passes.

For configuration changes:

  • .env.default is updated or already compatible with my changes
  • docker-compose.yml is updated or already compatible with my changes
  • I have included a list of my configuration changes in the PR description (under Changes)

…on credits tabs

Add the billing surface for the Settings V2 layout (SECRT-2272) with two tabs:
Subscription (plan, payment method, invoices, autopilot usage) and Automation
Credits (balance, auto top-up, usage, transaction history).

Backend:
- New GET /credits/invoices endpoint returning recent Stripe invoices for the
  current user, including hosted_invoice_url and invoice_pdf_url so the UI can
  link to Stripe-hosted views and PDF downloads.
- InvoiceListItem model on UserCreditBase; DisabledUserCredit returns [] so
  the UI degrades gracefully when credits are disabled.

Frontend:
- Billing page rebuilt around TabsLine with Subscription / Automation Credits.
- Per-feature card folders (Component.tsx + useComponent.ts) under
  components/SubscriptionTab and components/AutomationCreditsTab, following
  the project's "ComponentName/ComponentName.tsx + useComponentName.ts" rule.
- Shared helpers (formatCents, formatShortDate, EASE_OUT, formatRelativeReset)
  in billing/helpers.ts.
- TabsLineList: optional flush prop to remove first-tab left padding so the
  tab row aligns with the page heading.
- placeholder-pages test split: Billing now asserts the real heading + tabs,
  OAuth Apps keeps the "coming soon" assertion.

openapi.json regenerated to include the new /credits/invoices operation.
@Abhi1992002
Abhi1992002 requested a review from a team as a code owner April 29, 2026 12:38
@Abhi1992002
Abhi1992002 requested review from Bentlybro and kcze and removed request for a team April 29, 2026 12:38
@github-project-automation github-project-automation Bot moved this to 🆕 Needs initial review in AutoGPT development kanban Apr 29, 2026
@coderabbitai

coderabbitai Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a secured backend route to list Stripe invoices and related model types; expands the frontend billing UI with Subscription and Automation Credits tabs, multiple new cards/hooks (balance, usage, invoices, transactions, auto-refill, payment portal, plan), helper utilities, OpenAPI updates, and test adjustments.

Changes

Cohort / File(s) Summary
Backend: API & Model
autogpt_platform/backend/backend/api/features/v1.py, autogpt_platform/backend/backend/data/credit.py
Adds InvoiceListItem model and GET /credits/invoices route. Route authenticates via get_user_id, clamps limit, calls UserCredit.list_invoices which queries Stripe.Invoice.list (threadpool), logs/returns [] on Stripe errors, and maps invoices to the public schema with safe fallbacks.
Frontend: OpenAPI
autogpt_platform/frontend/src/app/api/openapi.json
Registers GET /api/credits/invoices operation and adds InvoiceListItem schema plus standard 401/422 responses.
Frontend: Billing page & tabs
.../settings/billing/page.tsx, .../__tests__/placeholder-pages.test.tsx
Replaces placeholder with tabbed billing UI (Subscription / Automation Credits); updates tests to explicit page-specific assertions and expands billing assertions for tabs and headings.
Frontend: Billing helpers
autogpt_platform/frontend/src/app/(platform)/settings/billing/helpers.ts
Adds EASE_OUT, formatCents, formatRelativeReset, and formatShortDate utilities used across billing components.
Frontend: Automation Credits (cards & hooks)
.../AutomationCreditsTab/*
Adds Balance, AutoRefill, Usage, TransactionHistory cards and their hooks. Hooks fetch/refetch credit data, handle top-ups and auto-refill config, aggregate usage buckets, and map transactions for UI.
Frontend: Subscription Tab (cards & hooks)
.../SubscriptionTab/*
Adds YourPlan, PaymentMethod, Invoices, AutopilotUsage cards and hooks for subscription state, portal URL handling, invoice display, autopilot usage visualization, and upgrade/cancel flows.
Frontend: Invoices UI & hook
.../SubscriptionTab/InvoicesCard/*
Client hook maps TOP_UP transactions into invoice rows; InvoicesCard renders table, status badges, and download button (opens PDF when available).
Frontend: Tabs component tweak
autogpt_platform/frontend/src/components/molecules/TabsLine/TabsLine.tsx
Adds flush?: boolean prop to TabsLineList to optionally remove left padding on the first tab trigger.
Tests
autogpt_platform/frontend/src/app/(platform)/settings/__tests__/placeholder-pages.test.tsx
Refactors tests to explicit page-specific tests and expands billing assertions for tab labels and headings.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client (Browser)
    participant API as Backend API
    participant Auth as Auth (get_user_id)
    participant Model as UserCreditModel
    participant Stripe as Stripe API

    Client->>API: GET /api/credits/invoices?limit=24 (with auth)
    API->>Auth: Validate token -> user_id
    Auth-->>API: user_id
    API->>Model: list_invoices(user_id, limit)
    Model->>Stripe: Invoice.list(customer=..., limit=...)
    Stripe-->>Model: invoices[]
    Model-->>API: mapped list[InvoiceListItem]
    API-->>Client: 200 OK, JSON array
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

Review effort 5/5

Suggested reviewers

  • Bentlybro
  • kcze
  • 0ubbe

Poem

🐰 I hopped through lines both front and back,
Invoices fetched, and cards in a stack,
Hooks that tally, tabs that gleam,
Stripe and UI stitched into a dream,
A tiny hop — deploy the beam!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a full billing page UI to settings V2 with two tabs (subscription and automation credits).
Description check ✅ Passed The description is well-structured and related to the changeset, explaining the why/what/how, listing all file changes, and providing a test plan.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch abhimanyuyadav/billing-page-for-settings-page

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions github-actions Bot added platform/frontend AutoGPT Platform - Front end platform/backend AutoGPT Platform - Back end labels Apr 29, 2026
@github-actions

github-actions Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Overlap Detection

This check compares your PR against all other open PRs targeting the same branch to detect potential merge conflicts early.

🔴 Merge Conflicts Detected

The following PRs have been tested and will have merge conflicts if merged after this PR. Consider coordinating with the authors.

  • feat(platform): Add AllQuiet alert integration alongside Discord alerts #11234 (ntindle · updated 19h ago)

    • .claude/skills/pr-test/SKILL.md (4 conflicts, ~56 lines)
    • autogpt_platform/backend/backend/api/features/chat/routes.py (8 conflicts, ~124 lines)
    • autogpt_platform/backend/backend/api/features/chat/routes_test.py (16 conflicts, ~904 lines)
    • autogpt_platform/backend/backend/api/features/subscription_routes_test.py (27 conflicts, ~1219 lines)
    • autogpt_platform/backend/backend/api/features/v1.py (9 conflicts, ~304 lines)
    • autogpt_platform/backend/backend/copilot/baseline/service.py (9 conflicts, ~405 lines)
    • autogpt_platform/backend/backend/copilot/baseline/service_unit_test.py (1 conflict, ~656 lines)
    • autogpt_platform/backend/backend/copilot/baseline/transcript_integration_test.py (1 conflict, ~129 lines)
    • autogpt_platform/backend/backend/copilot/config.py (2 conflicts, ~73 lines)
    • autogpt_platform/backend/backend/copilot/model_test.py (2 conflicts, ~183 lines)
    • autogpt_platform/backend/backend/copilot/pending_message_helpers.py (13 conflicts, ~148 lines)
    • autogpt_platform/backend/backend/copilot/pending_message_helpers_test.py (4 conflicts, ~125 lines)
    • autogpt_platform/backend/backend/copilot/pending_messages.py (14 conflicts, ~232 lines)
    • autogpt_platform/backend/backend/copilot/pending_messages_test.py (12 conflicts, ~124 lines)
    • autogpt_platform/backend/backend/copilot/prompting.py (2 conflicts, ~92 lines)
    • autogpt_platform/backend/backend/copilot/sdk/response_adapter.py (9 conflicts, ~373 lines)
    • autogpt_platform/backend/backend/copilot/sdk/response_adapter_test.py (4 conflicts, ~205 lines)
    • autogpt_platform/backend/backend/copilot/sdk/service.py (9 conflicts, ~123 lines)
    • autogpt_platform/backend/backend/copilot/sdk/service_test.py (2 conflicts, ~340 lines)
    • autogpt_platform/backend/backend/copilot/service.py (1 conflict, ~26 lines)
    • autogpt_platform/backend/backend/copilot/tools/__init__.py (1 conflict, ~4 lines)
    • autogpt_platform/backend/backend/copilot/tools/agent_guide_gate_test.py (3 conflicts, ~97 lines)
    • autogpt_platform/backend/backend/copilot/tools/edit_agent.py (1 conflict, ~21 lines)
    • autogpt_platform/backend/backend/copilot/tools/helpers.py (3 conflicts, ~168 lines)
    • autogpt_platform/backend/backend/copilot/tools/run_agent.py (1 conflict, ~10 lines)
    • autogpt_platform/backend/backend/data/credit.py (12 conflicts, ~841 lines)
    • autogpt_platform/backend/backend/data/credit_subscription_test.py (28 conflicts, ~2689 lines)
    • autogpt_platform/backend/backend/data/redis_helpers.py (5 conflicts, ~87 lines)
    • autogpt_platform/backend/backend/data/redis_helpers_test.py (3 conflicts, ~66 lines)
    • autogpt_platform/backend/backend/util/feature_flag.py (1 conflict, ~21 lines)
    • autogpt_platform/frontend/src/app/(platform)/build/components/BuilderChatPanel/__tests__/helpers.test.ts (deleted here, modified there)
    • autogpt_platform/frontend/src/app/(platform)/build/components/BuilderChatPanel/helpers.ts (deleted here, modified there)
    • autogpt_platform/frontend/src/app/(platform)/copilot/CopilotPage.tsx (2 conflicts, ~163 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/__tests__/useChatSession.test.ts (4 conflicts, ~55 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/__tests__/useCopilotPage.test.ts (16 conflicts, ~332 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/__tests__/useHydrateOnStreamEnd.test.ts (9 conflicts, ~376 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/__tests__/useLoadMoreMessages.test.ts (1 conflict, ~8 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/ChatContainer.tsx (3 conflicts, ~89 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatInput/ChatInput.tsx (3 conflicts, ~18 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx (2 conflicts, ~21 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx (6 conflicts, ~166 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/ReasoningCollapse.tsx (2 conflicts, ~51 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/ReasoningCollapse.test.tsx (2 conflicts, ~102 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/helpers.test.ts (2 conflicts, ~312 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/UsagePanelContent.tsx (1 conflict, ~9 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/usageHelpers.ts (1 conflict, ~9 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts (2 conflicts, ~70 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/queueFollowUpMessage.test.ts (8 conflicts, ~93 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts (2 conflicts, ~17 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/queueFollowUpMessage.ts (4 conflicts, ~97 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts (8 conflicts, ~193 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts (2 conflicts, ~116 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/useHydrateOnStreamEnd.ts (5 conflicts, ~191 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/useLoadMoreMessages.ts (2 conflicts, ~42 lines)
    • autogpt_platform/frontend/src/app/(platform)/layout.tsx (2 conflicts, ~16 lines)
    • autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/BriefingTabContent.tsx (7 conflicts, ~125 lines)
    • autogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/components/SubscriptionTierSection/SubscriptionTierSection.tsx (13 conflicts, ~198 lines)
    • autogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/components/SubscriptionTierSection/__tests__/SubscriptionTierSection.test.tsx (20 conflicts, ~550 lines)
    • autogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/components/SubscriptionTierSection/useSubscriptionTierSection.ts (4 conflicts, ~82 lines)
    • autogpt_platform/frontend/src/app/api/openapi.json (7 conflicts, ~222 lines)
    • docs/integrations/block-integrations/llm.md (7 conflicts, ~35 lines)
    • docs/integrations/block-integrations/misc.md (1 conflict, ~5 lines)
  • feat(platform): add first-class org/workspace support — schema, auth, APIs, migration, frontend #12670 (ntindle · updated 1d ago)

    • .claude/skills/pr-test/SKILL.md (4 conflicts, ~56 lines)
    • .gitignore (1 conflict, ~7 lines)
    • autogpt_platform/backend/.env.default (1 conflict, ~7 lines)
    • autogpt_platform/backend/backend/api/features/admin/rate_limit_admin_routes_test.py (3 conflicts, ~15 lines)
    • autogpt_platform/backend/backend/api/features/chat/routes.py (6 conflicts, ~94 lines)
    • autogpt_platform/backend/backend/api/features/chat/routes_test.py (21 conflicts, ~209 lines)
    • autogpt_platform/backend/backend/api/features/subscription_routes_test.py (27 conflicts, ~884 lines)
    • autogpt_platform/backend/backend/api/features/v1.py (13 conflicts, ~285 lines)
    • autogpt_platform/backend/backend/api/rest_api.py (2 conflicts, ~34 lines)
    • autogpt_platform/backend/backend/copilot/baseline/reasoning.py (10 conflicts, ~254 lines)
    • autogpt_platform/backend/backend/copilot/baseline/reasoning_test.py (6 conflicts, ~273 lines)
    • autogpt_platform/backend/backend/copilot/baseline/service.py (18 conflicts, ~376 lines)
    • autogpt_platform/backend/backend/copilot/baseline/service_unit_test.py (13 conflicts, ~246 lines)
    • autogpt_platform/backend/backend/copilot/baseline/transcript_integration_test.py (1 conflict, ~129 lines)
    • autogpt_platform/backend/backend/copilot/config.py (4 conflicts, ~118 lines)
    • autogpt_platform/backend/backend/copilot/executor/processor_test.py (2 conflicts, ~331 lines)
    • autogpt_platform/backend/backend/copilot/model_test.py (1 conflict, ~36 lines)
    • autogpt_platform/backend/backend/copilot/pending_message_helpers.py (13 conflicts, ~148 lines)
    • autogpt_platform/backend/backend/copilot/pending_message_helpers_test.py (4 conflicts, ~125 lines)
    • autogpt_platform/backend/backend/copilot/pending_messages.py (14 conflicts, ~232 lines)
    • autogpt_platform/backend/backend/copilot/pending_messages_test.py (12 conflicts, ~124 lines)
    • autogpt_platform/backend/backend/copilot/prompting.py (1 conflict, ~79 lines)
    • autogpt_platform/backend/backend/copilot/rate_limit.py (13 conflicts, ~227 lines)
    • autogpt_platform/backend/backend/copilot/rate_limit_test.py (2 conflicts, ~37 lines)
    • autogpt_platform/backend/backend/copilot/sdk/response_adapter.py (9 conflicts, ~373 lines)
    • autogpt_platform/backend/backend/copilot/sdk/response_adapter_test.py (4 conflicts, ~205 lines)
    • autogpt_platform/backend/backend/copilot/sdk/service.py (14 conflicts, ~239 lines)
    • autogpt_platform/backend/backend/copilot/sdk/service_test.py (2 conflicts, ~340 lines)
    • autogpt_platform/backend/backend/copilot/service.py (1 conflict, ~26 lines)
    • autogpt_platform/backend/backend/copilot/stream_registry_test.py (2 conflicts, ~84 lines)
    • autogpt_platform/backend/backend/copilot/tools/__init__.py (1 conflict, ~4 lines)
    • autogpt_platform/backend/backend/copilot/tools/agent_guide_gate_test.py (3 conflicts, ~73 lines)
    • autogpt_platform/backend/backend/copilot/tools/bash_exec.py (1 conflict, ~13 lines)
    • autogpt_platform/backend/backend/copilot/tools/helpers.py (4 conflicts, ~159 lines)
    • autogpt_platform/backend/backend/copilot/tools/tool_schema_test.py (1 conflict, ~20 lines)
    • autogpt_platform/backend/backend/data/credit.py (24 conflicts, ~336 lines)
    • autogpt_platform/backend/backend/data/credit_subscription_test.py (19 conflicts, ~1635 lines)
    • autogpt_platform/backend/backend/data/db_manager.py (2 conflicts, ~22 lines)
    • autogpt_platform/backend/backend/data/execution.py (4 conflicts, ~31 lines)
    • autogpt_platform/backend/backend/data/redis_helpers.py (5 conflicts, ~87 lines)
    • autogpt_platform/backend/backend/data/redis_helpers_test.py (3 conflicts, ~66 lines)
    • autogpt_platform/backend/backend/executor/cluster_lock.py (1 conflict, ~10 lines)
    • autogpt_platform/backend/backend/util/clients.py (1 conflict, ~12 lines)
    • autogpt_platform/backend/backend/util/feature_flag.py (1 conflict, ~10 lines)
    • autogpt_platform/backend/backend/util/settings.py (1 conflict, ~9 lines)
    • autogpt_platform/backend/poetry.lock (1 conflict, ~5 lines)
    • autogpt_platform/backend/schema.prisma (3 conflicts, ~338 lines)
    • autogpt_platform/backend/snapshots/get_rate_limit (1 conflict, ~5 lines)
    • autogpt_platform/backend/snapshots/reset_user_usage_daily_and_weekly (1 conflict, ~5 lines)
    • autogpt_platform/backend/snapshots/reset_user_usage_daily_only (1 conflict, ~5 lines)
    • autogpt_platform/frontend/src/app/(platform)/admin/diagnostics/__tests__/page.test.tsx (3 conflicts, ~330 lines)
    • autogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/__tests__/RateLimitDisplay.test.tsx (1 conflict, ~5 lines)
    • autogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/__tests__/RateLimitManager.test.tsx (2 conflicts, ~10 lines)
    • autogpt_platform/frontend/src/app/(platform)/admin/rate-limits/components/__tests__/useRateLimitManager.test.ts (1 conflict, ~5 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/CopilotPage.tsx (4 conflicts, ~186 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/__tests__/CopilotPage.test.tsx (1 conflict, ~5 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/__tests__/useChatSession.test.ts (4 conflicts, ~55 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/__tests__/useCopilotPage.test.ts (16 conflicts, ~332 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/__tests__/useHydrateOnStreamEnd.test.ts (9 conflicts, ~376 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/__tests__/useLoadMoreMessages.test.ts (1 conflict, ~8 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatContainer/ChatContainer.tsx (3 conflicts, ~89 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatInput/ChatInput.tsx (3 conflicts, ~18 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/ChatMessagesContainer.tsx (2 conflicts, ~21 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/__tests__/ChatMessagesContainer.test.tsx (6 conflicts, ~166 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/ReasoningCollapse.tsx (2 conflicts, ~51 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/components/__tests__/ReasoningCollapse.test.tsx (2 conflicts, ~102 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/ChatMessagesContainer/helpers.test.ts (2 conflicts, ~312 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/UsagePanelContent.tsx (7 conflicts, ~59 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsageLimits.test.tsx (1 conflict, ~5 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/components/UsageLimits/__tests__/UsagePanelContentRender.test.tsx (1 conflict, ~5 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/convertChatSessionToUiMessages.test.ts (2 conflicts, ~70 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/__tests__/queueFollowUpMessage.test.ts (8 conflicts, ~93 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/convertChatSessionToUiMessages.ts (2 conflicts, ~17 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/helpers/queueFollowUpMessage.ts (4 conflicts, ~97 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/tools/GenericTool/__tests__/GenericTool.test.tsx (2 conflicts, ~206 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotPage.ts (8 conflicts, ~193 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/useCopilotStream.ts (2 conflicts, ~116 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/useHydrateOnStreamEnd.ts (5 conflicts, ~191 lines)
    • autogpt_platform/frontend/src/app/(platform)/copilot/useLoadMoreMessages.ts (2 conflicts, ~42 lines)
    • autogpt_platform/frontend/src/app/(platform)/layout.tsx (2 conflicts, ~16 lines)
    • autogpt_platform/frontend/src/app/(platform)/library/components/AgentBriefingPanel/__tests__/BriefingTabContent.test.tsx (1 conflict, ~5 lines)
    • autogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/components/SubscriptionTierSection/SubscriptionTierSection.tsx (9 conflicts, ~118 lines)
    • autogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/components/SubscriptionTierSection/__tests__/SubscriptionTierSection.test.tsx (38 conflicts, ~483 lines)
    • autogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/components/SubscriptionTierSection/components/PendingChangeBanner/PendingChangeBanner.tsx (1 conflict, ~5 lines)
    • autogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/components/SubscriptionTierSection/helpers.ts (6 conflicts, ~79 lines)
    • autogpt_platform/frontend/src/app/(platform)/profile/(user)/credits/components/SubscriptionTierSection/useSubscriptionTierSection.ts (2 conflicts, ~34 lines)
    • autogpt_platform/frontend/src/app/api/openapi.json (9 conflicts, ~289 lines)
    • autogpt_platform/frontend/src/lib/autogpt-server-api/helpers.test.ts (2 conflicts, ~39 lines)
    • docs/integrations/block-integrations/llm.md (7 conflicts, ~35 lines)
    • docs/integrations/block-integrations/misc.md (1 conflict, ~5 lines)
  • fix(copilot): prevent 524 timeout on chat deletion by deferring cleanup #12668 (Otto-AGPT · updated 13d ago)

🟡 Medium Risk — Some Line Overlap

These PRs have some overlapping changes:

🟢 Low Risk — File Overlap Only

These PRs touch the same files but different sections (click to expand)

Summary: 3 conflict(s), 1 medium risk, 2 low risk (out of 6 PRs with file overlap)


Auto-generated on push. Ignores: openapi.json, lock files.

Comment thread autogpt_platform/frontend/src/app/(platform)/settings/billing/helpers.ts Outdated
Comment thread autogpt_platform/backend/backend/data/credit.py
Comment thread autogpt_platform/backend/backend/data/credit.py
@codecov

codecov Bot commented Apr 29, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.31183% with 59 lines in your changes missing coverage. Please review.
✅ Project coverage is 69.62%. Comparing base (c08b977) to head (85e470d).
⚠️ Report is 4 commits behind head on dev.

Additional details and impacted files
@@            Coverage Diff             @@
##              dev   #12942      +/-   ##
==========================================
+ Coverage   69.56%   69.62%   +0.06%     
==========================================
  Files        2114     2135      +21     
  Lines      157428   157922     +494     
  Branches    16230    16317      +87     
==========================================
+ Hits       109509   109953     +444     
- Misses      44696    44725      +29     
- Partials     3223     3244      +21     
Flag Coverage Δ
platform-backend 78.60% <95.90%> (+0.02%) ⬆️
platform-frontend 31.60% <84.25%> (+0.67%) ⬆️
platform-frontend-e2e 30.94% <100.00%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Platform Backend 78.60% <95.90%> (+0.02%) ⬆️
Platform Frontend 37.87% <84.25%> (+0.57%) ⬆️
AutoGPT Libs ∅ <ø> (∅)
Classic AutoGPT 28.43% <ø> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 14

🧹 Nitpick comments (4)
autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/TransactionHistoryCard/useTransactionHistoryCard.ts (1)

31-33: Use a deterministic fallback ID.

Falling back to Math.random() makes row IDs change across renders, which is unstable if this value is used as a React key. Prefer a deterministic fallback derived from the transaction fields so rows don't remount unnecessarily.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/AutomationCreditsTab/TransactionHistoryCard/useTransactionHistoryCard.ts
around lines 31 - 33, The current id assignment in useTransactionHistoryCard
uses Math.random() as a final fallback which produces non-deterministic IDs;
replace that with a stable deterministic fallback derived from transaction
fields (e.g., combine tx.transaction_time, tx.amount, tx.transaction_type,
tx.transaction_key or a short hash of JSON.stringify(tx)) so the id expression
(currently: id: tx.transaction_key ?? `${tx.transaction_time?.toString() ??
Math.random()}`) becomes something like tx.transaction_key ??
`${tx.transaction_time ?? ''}-${tx.amount ?? ''}-${tx.transaction_type ?? ''}`
or call a small helper computeStableId(tx) that returns a string hash — update
the id assignment to use that helper to ensure keys remain stable across
renders.
autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/YourPlanCard/useYourPlanCard.ts (1)

52-58: Tighten changeTier typing to remove the cast.

changeTier(tier: string) plus as SubscriptionTierRequestTier weakens type safety. Accept SubscriptionTierRequestTier directly and pass tier without casting.

Proposed fix
-  async function changeTier(tier: string) {
+  async function changeTier(tier: SubscriptionTierRequestTier) {
@@
-        tier: tier as SubscriptionTierRequestTier,
+        tier,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/SubscriptionTab/YourPlanCard/useYourPlanCard.ts
around lines 52 - 58, changeTier currently accepts a plain string and then casts
it to SubscriptionTierRequestTier which weakens type safety; change the function
signature of changeTier to accept tier: SubscriptionTierRequestTier and pass
tier directly to updateTier without the as cast (updateTier({ data: { tier,
success_url: ..., cancel_url: ... } })). Ensure any call sites of changeTier are
updated to provide a SubscriptionTierRequestTier where necessary.
autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/InvoicesCard/useInvoicesCard.next.ts (1)

1-5: Remove the transitional .next hook file after switching useInvoicesCard.ts.

Keeping two near-identical source hooks increases drift risk and makes future fixes easy to apply to the wrong file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/SubscriptionTab/InvoicesCard/useInvoicesCard.next.ts
around lines 1 - 5, Delete the transitional hook file useInvoicesCard.next.ts
and consolidate the implementation into useInvoicesCard.ts so there are not two
near-identical hooks; specifically remove the file referenced in the diff,
ensure any imports or references to useInvoicesCard.next (e.g., from
InvoicesCard.tsx or other callers) now point to useInvoicesCard (the canonical
hook), and run a quick search to update/replace any remaining usages to avoid
drift and duplicate maintenance.
autogpt_platform/frontend/src/app/(platform)/settings/billing/helpers.ts (1)

43-253: Split mock billing fixtures out of helpers.ts.

This module currently mixes formatting utilities with large static sample datasets (including fixed dates like Line 160). Move sample data to dedicated fixtures/mocks so runtime helpers stay focused and less error-prone.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@autogpt_platform/frontend/src/app/`(platform)/settings/billing/helpers.ts
around lines 43 - 253, The helpers.ts mixes runtime utilities with large static
mock datasets; extract all sample data (SEED_AMOUNTS, DAILY_USAGE,
CREDIT_TRANSACTIONS, INVOICES, AUTOMATION_CREDITS_BALANCE, PAYMENT_METHOD,
CURRENT_PLAN, AUTOPILOT_USAGE, and any related interfaces/consts used only for
fixtures) into a new billing fixtures module and import them from helpers.ts so
helpers only exports pure helper functions/types; update references to
DAILY_USAGE and the other constants in helpers.ts to import from the new
fixtures module and remove hardcoded fixed dates from runtime logic (keep them
only in fixtures) so helpers remain focused and deterministic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@autogpt_platform/backend/backend/data/credit.py`:
- Around line 1175-1188: The current mapping in the invoice conversion (creating
InvoiceListItem) uses invoice.amount_paid for amount_paid_cents which shows 0
for open invoices; update the mapping in the function that builds
InvoiceListItem (the block creating InvoiceListItem with id, number, created_at,
amount_paid_cents, etc.) to expose the invoice total instead (map invoice.total
to the returned amount field) or include both fields explicitly (e.g., add
total_cents=invoice.total and keep amount_paid_cents=invoice.amount_paid) so the
UI can display outstanding/open invoice amounts correctly.
- Around line 1163-1173: list_invoices currently calls get_stripe_customer_id
which will create a Stripe customer if missing; change list_invoices so it does
not create customers: add or use a non-creating lookup (e.g.,
get_stripe_customer_id(user_id, create_if_missing=False) or a new helper like
fetch_existing_stripe_customer_id) to only read an existing stripe_customer_id,
and if none is found return [] immediately; only call
run_in_threadpool(stripe.Invoice.list, customer=customer_id, limit=limit) when a
customer_id is present.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/AutomationCreditsTab/AutoRefillCard/useAutoRefillCard.ts:
- Around line 45-57: The client-side validation in the AutoRefillCard component
currently only enforces minimums and lets refillValue be less than
thresholdValue; update the isValid expression to also require refillValue >=
thresholdValue (comparing the displayed numeric values used by thresholdValue
and refillValue) so the Save flow (save and configureAutoTopUp) is blocked for
invalid combos; adjust the isValid variable that’s used by save() (and any UI
disable logic) to include this additional check.
- Around line 51-66: Wrap the async handlers save() and disable() in try/catch
(or return the promise) so mutation failures are not converted to unhandled
rejections; specifically, in save() (which checks isValid, calls
configureAutoTopUp, refetch, and setOpen) and disable() (which calls
configureAutoTopUp, refetch, setOpen) catch any errors from
configureAutoTopUp/refetch, call the UI toast error notifier (e.g., toast.error
or the project's showToast) with a clear message including the error, and avoid
closing the UI (setOpen) on failure; alternatively return the promises so the
calling component can handle errors—ensure you reference configureAutoTopUp,
refetch, save, and disable when making the change.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/AutomationCreditsTab/BalanceCard/useBalanceCard.ts:
- Around line 11-18: The hook currently masks fetch failures by collapsing the
credits result to 0; update useBalanceCard (the hook using
useGetV1GetUserCredits and the balanceCents value) to surface the query error
state instead of defaulting to zero: stop converting a failed/errored response
to 0, return the original isLoading, data (possibly undefined), and error from
useGetV1GetUserCredits so the BalanceCard consumer can render an error state;
update consumers to show the ErrorCard when error is present (per guidelines)
rather than treating undefined/error as a zero balance.
- Around line 25-37: The top-up validation and error handling need to match the
backend: change the client-side check (numericAmount and isValid) to require a
whole-dollar integer >= 5 (e.g., use Number.isFinite(numericAmount) &&
Number.isInteger(numericAmount) && numericAmount >= 5) so amounts like 5.25 are
rejected; update the handleSubmit flow (function handleSubmit, call to
requestTopUp, setOpen) to wrap the async mutation in try/catch, await the
requestTopUp call, show a toast error on any failure (using the app's toast
helper) and only proceed to use the returned checkout_url and close the modal on
success; apply the same validation and error-handling changes to the other
occurrence referenced (lines 47-48).

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/AutomationCreditsTab/TransactionHistoryCard/TransactionHistoryCard.tsx:
- Around line 17-21: The hook useTransactionHistoryCard currently returns only
transactions and isLoading; update it to surface the underlying query's status
(e.g., return isSuccess and/or error from useGetV1GetCreditHistory) so callers
can detect failures, and then update TransactionHistoryCard to check that
status: keep the existing Skeleton while isLoading, and render <ErrorCard />
when the hook indicates failure (i.e., !isSuccess or error) before trying to
render transactions; reference useTransactionHistoryCard,
useGetV1GetCreditHistory, TransactionHistoryCard, Skeleton, and ErrorCard when
making the changes.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/AutomationCreditsTab/UsageCard/UsageCard.tsx:
- Around line 22-25: The current early return in UsageCard.tsx (the lines using
useUsageCard() and `if (!hasUsage) return null;`) hides the entire monthly usage
card for zero-usage accounts; instead remove that early return and render the
UsageCard component for all users, using the `hasUsage` flag to render a proper
zero-state (e.g., show usage values as 0 or a "No activity this month" message)
while still rendering real usage when `hasUsage` is true; update the JSX inside
the UsageCard component to conditionally display the zero-state UI using `usage`
and `hasUsage` (from useUsageCard) rather than returning null.
- Around line 115-117: The TooltipTrigger child in UsageCard is a plain div and
not keyboard-focusable; make the chart bar wrapper focusable so keyboard users
can open the tooltip: update the TooltipTrigger child (the div with className
"group flex h-full flex-1 flex-col justify-end") to either be a semantic
focusable element (e.g., a button that forwards props) or add tabIndex={0} and
an appropriate role (e.g., role="button"), and implement onKeyDown (Enter/Space)
to mirror the click/hover behavior used by the tooltip; ensure this change
preserves the asChild pattern and works with the existing motion.div inside the
wrapper.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/AutomationCreditsTab/UsageCard/useUsageCard.ts:
- Around line 27-53: The buckets are created using the local "today" but keys
are generated from transactions with txDate.toISOString() (UTC), causing day
mismatches across time zones; update both bucket creation and transaction keying
to use the same local-date key format (e.g., YYYY-MM-DD derived from local date
components or toLocaleDateString('en-CA')) so keys are computed in local time
consistently; adjust the code in useUsageCard.ts where today, buckets, and key
(derived from txDate) are set so both use the identical local-date routine
(reference WINDOW_DAYS, DAY_MS, today, buckets, txDate, and key).

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/SubscriptionTab/AutopilotUsageCard/AutopilotUsageCard.tsx:
- Line 19: The tooltip text in the AutopilotUsageCard component currently uses a
dead markdown link ("[Learn more about usage](#)"); update the string inside the
AutopilotUsageCard component (the tooltip/description constant or JSX prop
containing "Each Autopilot request..." and the markdown link) to point to the
real documentation URL (e.g., your public docs page or internal help route)
instead of "#", preserving the markdown/link formatting so the link remains
clickable in the UI.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/SubscriptionTab/InvoicesCard/InvoicesCard.tsx:
- Line 67: The empty table header for the action column in the InvoicesCard
component (the <th className="w-12 px-4 py-3" /> element) needs an accessible
label; update that <th> to include a descriptive, screen-reader-only label
(e.g., "Actions" or "Download invoice") — either by adding a visually-hidden
<span> (using the app's existing "sr-only" utility) inside the <th> or by adding
an aria-label/aria-labelledby to the <th> so assistive tech can announce the
column name.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/SubscriptionTab/InvoicesCard/useInvoicesCard.ts:
- Around line 3-7: Replace the hand-rolled TOP_UP-based invoice construction in
useInvoicesCard (which currently builds rows and drops hosted/PDF URLs) with the
canonical generated API hook useGetV1ListInvoices: call useGetV1ListInvoices to
fetch invoices, map the returned invoice shape directly into the rows
(preserving hosted_url/pdf_url fields and invoice-specific metadata), and remove
the TOP_UP filtering logic; use the ready-to-paste implementation in
useInvoicesCard.next.ts as a reference, regenerate the client with pnpm
generate:api if the hook is missing, and ensure the mapping preserves
subscription invoices and their hosted/PDF URLs.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/SubscriptionTab/YourPlanCard/useYourPlanCard.ts:
- Around line 52-65: The changeTier function currently lets updateTier errors
bubble silently; wrap the updateTier call in a try/catch (or attach an onError
handler) inside changeTier so mutation failures show a user toast (use your
app's toast utility), call Sentry.captureException(err) for the caught
exception, and ensure subscription.refetch() runs in a finally block (or only
after a successful redirect flow); apply the same guarded pattern to the other
tier-related mutation at the 72-73 area (refer to the same updateTier /
subscription.refetch usage) and do not replace UI-rendered errors with
toasts—use ErrorCard where rendering persistent UI errors is required.

---

Nitpick comments:
In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/AutomationCreditsTab/TransactionHistoryCard/useTransactionHistoryCard.ts:
- Around line 31-33: The current id assignment in useTransactionHistoryCard uses
Math.random() as a final fallback which produces non-deterministic IDs; replace
that with a stable deterministic fallback derived from transaction fields (e.g.,
combine tx.transaction_time, tx.amount, tx.transaction_type, tx.transaction_key
or a short hash of JSON.stringify(tx)) so the id expression (currently: id:
tx.transaction_key ?? `${tx.transaction_time?.toString() ?? Math.random()}`)
becomes something like tx.transaction_key ?? `${tx.transaction_time ??
''}-${tx.amount ?? ''}-${tx.transaction_type ?? ''}` or call a small helper
computeStableId(tx) that returns a string hash — update the id assignment to use
that helper to ensure keys remain stable across renders.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/SubscriptionTab/InvoicesCard/useInvoicesCard.next.ts:
- Around line 1-5: Delete the transitional hook file useInvoicesCard.next.ts and
consolidate the implementation into useInvoicesCard.ts so there are not two
near-identical hooks; specifically remove the file referenced in the diff,
ensure any imports or references to useInvoicesCard.next (e.g., from
InvoicesCard.tsx or other callers) now point to useInvoicesCard (the canonical
hook), and run a quick search to update/replace any remaining usages to avoid
drift and duplicate maintenance.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/SubscriptionTab/YourPlanCard/useYourPlanCard.ts:
- Around line 52-58: changeTier currently accepts a plain string and then casts
it to SubscriptionTierRequestTier which weakens type safety; change the function
signature of changeTier to accept tier: SubscriptionTierRequestTier and pass
tier directly to updateTier without the as cast (updateTier({ data: { tier,
success_url: ..., cancel_url: ... } })). Ensure any call sites of changeTier are
updated to provide a SubscriptionTierRequestTier where necessary.

In `@autogpt_platform/frontend/src/app/`(platform)/settings/billing/helpers.ts:
- Around line 43-253: The helpers.ts mixes runtime utilities with large static
mock datasets; extract all sample data (SEED_AMOUNTS, DAILY_USAGE,
CREDIT_TRANSACTIONS, INVOICES, AUTOMATION_CREDITS_BALANCE, PAYMENT_METHOD,
CURRENT_PLAN, AUTOPILOT_USAGE, and any related interfaces/consts used only for
fixtures) into a new billing fixtures module and import them from helpers.ts so
helpers only exports pure helper functions/types; update references to
DAILY_USAGE and the other constants in helpers.ts to import from the new
fixtures module and remove hardcoded fixed dates from runtime logic (keep them
only in fixtures) so helpers remain focused and deterministic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 910e6c41-db58-4602-b58a-5583cd6f069f

📥 Commits

Reviewing files that changed from the base of the PR and between c3c2737 and 6b22d37.

📒 Files selected for processing (27)
  • autogpt_platform/backend/backend/api/features/v1.py
  • autogpt_platform/backend/backend/data/credit.py
  • autogpt_platform/frontend/src/app/(platform)/settings/__tests__/placeholder-pages.test.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/AutoRefillCard/AutoRefillCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/AutoRefillCard/AutoRefillDialog.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/AutoRefillCard/useAutoRefillCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/AutomationCreditsTab.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/BalanceCard/BalanceCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/BalanceCard/useBalanceCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/TransactionHistoryCard/TransactionHistoryCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/TransactionHistoryCard/useTransactionHistoryCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/UsageCard/UsageCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/UsageCard/useUsageCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/AutopilotUsageCard/AutopilotUsageCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/AutopilotUsageCard/useAutopilotUsageCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/InvoicesCard/InvoicesCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/InvoicesCard/useInvoicesCard.next.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/InvoicesCard/useInvoicesCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/PaymentMethodCard/PaymentMethodCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/PaymentMethodCard/usePaymentMethodCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/SubscriptionTab.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/YourPlanCard/YourPlanCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/YourPlanCard/useYourPlanCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/helpers.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/page.tsx
  • autogpt_platform/frontend/src/app/api/openapi.json
  • autogpt_platform/frontend/src/components/molecules/TabsLine/TabsLine.tsx

Comment thread autogpt_platform/backend/backend/data/credit.py Outdated
Comment thread autogpt_platform/backend/backend/data/credit.py
- credit.py: list_invoices skips Stripe call (and the implicit
  customer-create) for users without a stripe_customer_id, fixing the
  Sentry-flagged orphaned-customer bug for BetaUserCredit users; wraps
  the Stripe call in try/except so a Stripe outage degrades to [].
- helpers.ts: drop the unused mock data + interfaces (PlanInfo,
  PaymentMethod, Invoice, AutopilotUsage*, CreditTransaction, DailyUsage)
  and their seeded constants. Every card hook reads from real APIs;
  the constants were dead bundle weight (and PAYMENT_METHOD even
  embedded a real-looking name).
- useTransactionHistoryCard.ts: replace Math.random() row-key fallback
  with a stable index-based id so React doesn't remount rows.
- InvoicesCard.tsx: add noreferrer to window.open so the Stripe-hosted
  PDF tab does not receive our origin in the Referer header.
- useInvoicesCard.next.ts: deleted; left a TODO in useInvoicesCard.ts
  pointing at the post-generate:api swap so we don't ship two
  implementations of the same hook.
- useYourPlanCard.ts / YourPlanCard.tsx: derive nextTier from the
  current tier instead of always upgrading to PRO; hide the "Upgrade
  plan" button entirely on the top tier so the click can't no-op.
- credit.py / openapi.json: InvoiceListItem now exposes total_cents
  (mapped from invoice.total) alongside amount_paid_cents so open/
  unpaid invoices show the correct displayed amount.
- useBalanceCard: enforce whole-dollar minimum-$5 top-ups (Number.isInteger
  guard), wrap requestTopUp in try/catch + destructive toast, expose
  isError so the consumer can render a real error state.
- BalanceCard.tsx: render ErrorCard on isError or null balance instead
  of silently displaying $0; tighten the dialog input to whole dollars.
- useAutoRefillCard: add refillValue >= thresholdValue to isValid
  (mirrors the backend 422), wrap save/disable mutations in try/catch
  with destructive toasts.
- useTransactionHistoryCard: expose isError + refetch.
- TransactionHistoryCard.tsx: render ErrorCard on fetch failure.
- useUsageCard: replace toISOString-based bucket keys with a local-date
  key so usage doesn't shift across timezones; skip transactions with
  invalid dates.
- UsageCard.tsx: gate on isLoading instead of hasUsage so zero-activity
  accounts still see the monthly usage card; replace each chart bar's
  div trigger with a focusable <button> + aria-label so keyboard users
  can reach the per-day tooltip.
- AutopilotUsageCard.tsx: drop the dead "[Learn more](#)" markdown
  link from the explainer copy.
- InvoicesCard.tsx: add an sr-only "Download" label inside the empty
  action <th> so the column has an accessible name.
- useYourPlanCard: wrap changeTier in try/catch and surface a
  destructive toast so failed Stripe calls don't become unhandled
  rejections.
…n unmount

Sentry flagged that subscription.refetch() was awaited after navigating
via window.location.href, which would set state on an unmounting tree
while Stripe Checkout takes over. Return early when a redirect URL is
present; only refetch when staying on the page.
@Abhi1992002

Copy link
Copy Markdown
Member Author

Thanks for the summary. The 4 nitpicks were addressed across the polish commits:

  • Math.random() ID → fixed in b700d1ac1 (now tx-${idx} / top-up-${idx}).
  • changeTier typing / drop the cast → fixed in 952136achangeTier takes SubscriptionTierRequestTier directly, TIER_ORDER is satisfies readonly SubscriptionTierRequestTier[], no as cast left.
  • Remove useInvoicesCard.next.ts → already deleted in b700d1ac1; intent (post-generate:api swap) lives as a TODO at the top of useInvoicesCard.ts.
  • Split mock fixtures out of helpers.ts → already deleted in b700d1ac1; helpers.ts now only exports EASE_OUT, formatCents, formatRelativeReset, formatShortDate. No fixtures live in runtime modules.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/TransactionHistoryCard/TransactionHistoryCard.tsx (2)

121-127: Use a named props type/interface for Th instead of an inline anonymous object.

At Line 124, the inline prop type works but is less consistent with the codebase’s props conventions and makes reuse harder. Prefer a named ThProps declaration.

As per coding guidelines: Component props should use a named Props type/interface (non-exported unless needed outside the component).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/AutomationCreditsTab/TransactionHistoryCard/TransactionHistoryCard.tsx
around lines 121 - 127, The Th component currently declares its props inline;
extract that anonymous type into a named non-exported interface or type (e.g.,
ThProps) and use it for the Th signature to match project conventions. Update
the function declaration from Th({ children, align = "left" }: { children:
React.ReactNode; align?: "left" | "right"; }) to Th(props: ThProps) (or keep
destructuring) where ThProps is defined above as type ThProps = { children:
React.ReactNode; align?: "left" | "right"; }; reference the Th function and the
new ThProps type when making the change.

16-119: Consider extracting the table view into a sub-component to reduce render complexity.

TransactionHistoryCard currently combines state branching, animation config, and full table markup in one render path. This is above the repo’s “~50 lines” guidance and will be harder to maintain as billing UI evolves.

♻️ Suggested refactor sketch
 export function TransactionHistoryCard({ index = 0 }: Props) {
   const reduceMotion = useReducedMotion();
   const { transactions, isLoading, isError, refetch } =
     useTransactionHistoryCard();

   if (isLoading) {
     return <Skeleton className="h-[200px] rounded-[18px]" />;
   }

   if (isError) {
     return (
       <ErrorCard
         context="transaction history"
         hint="We couldn't load your recent transactions."
         onRetry={() => void refetch()}
       />
     );
   }

   return (
     <motion.section ...>
-      <div className="overflow-hidden rounded-[18px] border ...">
-        {transactions.length === 0 ? ( ... ) : ( <table>...</table> )}
-      </div>
+      <TransactionHistoryTable transactions={transactions} />
     </motion.section>
   );
 }
+
+type TransactionHistoryTableProps = {
+  transactions: ReturnType<typeof useTransactionHistoryCard>["transactions"];
+};
+
+function TransactionHistoryTable({ transactions }: TransactionHistoryTableProps) {
+  if (transactions.length === 0) {
+    return (
+      <div className="overflow-hidden rounded-[18px] border border-zinc-200 bg-white shadow-[0_1px_2px_rgba(15,15,20,0.04)]">
+        <div className="px-4 py-6">
+          <Text variant="small" as="span" className="text-zinc-500">
+            No transactions yet.
+          </Text>
+        </div>
+      </div>
+    );
+  }
+  // existing table markup
+}

As per coding guidelines: Keep render functions and hooks under ~50 lines; extract named helpers or sub-components when they grow longer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/AutomationCreditsTab/TransactionHistoryCard/TransactionHistoryCard.tsx
around lines 16 - 119, TransactionHistoryCard is too large—extract the table
rendering into a sub-component to keep the main render/hooks under ~50 lines.
Create a new component (e.g., TransactionTable or TransactionHistoryList) that
accepts transactions (and optional row click handlers) and move the entire
<table> ... </table> markup including the transactions.map(...) row rendering
and related className logic there; keep TransactionHistoryCard responsible for
hooks (useTransactionHistoryCard), loading/error branching, and the motion
wrapper, and pass reduceMotion/index only if the sub-component needs animation
context. Ensure you reference transaction.id, transaction.date,
transaction.description, transaction.amount, transaction.balance, and
transaction.kind in the new component and update the JSX to render
<TransactionTable transactions={transactions} /> from TransactionHistoryCard.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/AutomationCreditsTab/AutoRefillCard/useAutoRefillCard.ts:
- Around line 44-60: The validation currently allows fractional dollar inputs
like 5.5; update the isValid logic to reject non-whole-dollar values by ensuring
thresholdValue and refillValue are integers (e.g., add
Number.isInteger(thresholdValue) && Number.isInteger(refillValue) to the
existing checks). Keep the rest of the gating (>=5, refillValue >=
thresholdValue) and leave save/configureAutoTopUp unchanged.

In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/SubscriptionTab/InvoicesCard/InvoicesCard.tsx:
- Around line 28-33: The component currently only checks isLoading and then
renders the empty-state when invoices is empty, so update the use of the hook
useInvoicesCard() to also return (or expose) an error value (e.g., error) and in
InvoicesCard.tsx add a branch that, when error is truthy and isLoading is false,
renders the <ErrorCard /> component instead of the “No invoices yet.” copy;
ensure you reference the hook's returned error (add it to the destructure
alongside invoices and isLoading) and use that error branch before rendering the
empty-state UI.

---

Nitpick comments:
In
`@autogpt_platform/frontend/src/app/`(platform)/settings/billing/components/AutomationCreditsTab/TransactionHistoryCard/TransactionHistoryCard.tsx:
- Around line 121-127: The Th component currently declares its props inline;
extract that anonymous type into a named non-exported interface or type (e.g.,
ThProps) and use it for the Th signature to match project conventions. Update
the function declaration from Th({ children, align = "left" }: { children:
React.ReactNode; align?: "left" | "right"; }) to Th(props: ThProps) (or keep
destructuring) where ThProps is defined above as type ThProps = { children:
React.ReactNode; align?: "left" | "right"; }; reference the Th function and the
new ThProps type when making the change.
- Around line 16-119: TransactionHistoryCard is too large—extract the table
rendering into a sub-component to keep the main render/hooks under ~50 lines.
Create a new component (e.g., TransactionTable or TransactionHistoryList) that
accepts transactions (and optional row click handlers) and move the entire
<table> ... </table> markup including the transactions.map(...) row rendering
and related className logic there; keep TransactionHistoryCard responsible for
hooks (useTransactionHistoryCard), loading/error branching, and the motion
wrapper, and pass reduceMotion/index only if the sub-component needs animation
context. Ensure you reference transaction.id, transaction.date,
transaction.description, transaction.amount, transaction.balance, and
transaction.kind in the new component and update the JSX to render
<TransactionTable transactions={transactions} /> from TransactionHistoryCard.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 32a212ec-64cc-4f9e-b94f-16e8ccc88fa5

📥 Commits

Reviewing files that changed from the base of the PR and between b700d1a and 952136a.

📒 Files selected for processing (12)
  • autogpt_platform/backend/backend/data/credit.py
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/AutoRefillCard/useAutoRefillCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/BalanceCard/BalanceCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/BalanceCard/useBalanceCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/TransactionHistoryCard/TransactionHistoryCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/TransactionHistoryCard/useTransactionHistoryCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/UsageCard/UsageCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/UsageCard/useUsageCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/AutopilotUsageCard/AutopilotUsageCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/InvoicesCard/InvoicesCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/YourPlanCard/useYourPlanCard.ts
  • autogpt_platform/frontend/src/app/api/openapi.json
🚧 Files skipped from review as they are similar to previous changes (7)
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/UsageCard/useUsageCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/UsageCard/UsageCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/AutopilotUsageCard/AutopilotUsageCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/BalanceCard/BalanceCard.tsx
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/SubscriptionTab/YourPlanCard/useYourPlanCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/BalanceCard/useBalanceCard.ts
  • autogpt_platform/frontend/src/app/(platform)/settings/billing/components/AutomationCreditsTab/TransactionHistoryCard/useTransactionHistoryCard.ts

Resolves the lint job failure on CI.
@Abhi1992002

Copy link
Copy Markdown
Member Author

Coverage update — codecov gate passed.

After df501ad (test additions), all 14 codecov checks now report pass:

  • codecov/patch/platform-backend
  • codecov/patch/platform-frontend
  • codecov/patch/Platform Backend / codecov/patch/Platform Frontend
  • codecov/project/* (4 variants) ✅
  • codecov/patch/Classic AutoGPT / AutoGPT Libs / classic / autogpt-libs

Coverage breakdown for this patch:

  • Backend (credit.py + v1.py changes): unit tests in credit_invoices_test.py (no-customer skip, StripeError → [], total_cents mapping, limit clamp) + endpoint tests in v1_test.py (default limit, payload shape, ?limit=500 → 422, ?limit=24 forwarded).
  • Frontend (20 new card/hook files): per-card render tests in billing-cards.test.tsx, every mutation path covered via renderHook in billing-hooks.test.tsx (handleSubmit success/error/no-op, save/disable success/error/short-circuit, changeTier success/error/BASIC-no-op, label fallbacks), pure-JS coverage of all formatters in helpers.test.ts.
  • Local v8 (cobertura) on the new billing source: 90.1% (201/223 lines).

Final state: 37 CI checks pass, 0 fail, 5 long-pole tests still building (test (3.11/3.12/3.13), end-to-end tests, Check PR Status). All 29 review threads resolved, all 4 bot reviews acknowledged, mergeable.

@ntindle

ntindle commented Apr 29, 2026

Copy link
Copy Markdown
Member

for testing this how do i get a stripe enabled account?

@Abhi1992002

Copy link
Copy Markdown
Member Author

Hey @ntindle — here's the full local Stripe setup for testing this PR:

1. Stripe test account

  1. Sign up at https://dashboard.stripe.com (free, no card required).
  2. Toggle Test mode ON (top-right of dashboard). All keys/products in test mode are sandboxed.
  3. Grab your test secret key from Developers → API keys (sk_test_...).

2. Create test products + prices

Dashboard → Product catalog → Add product. One per tier you want to exercise:

Tier Suggested
BASIC $10/mo recurring
PRO $30/mo recurring
MAX $100/mo recurring
BUSINESS $500/mo recurring

Copy each price_xxx after create.

3. LaunchDarkly tier → price mapping

Backend reads price IDs from LD flag, not env (see credit.py:get_subscription_price_id).

In LaunchDarkly → flag copilot-tier-stripe-prices → JSON variation:

{
  "BASIC": "price_1Abc...",
  "PRO":   "price_1Def...",
  "MAX":   "price_1Ghi...",
  "BUSINESS": "price_1Jkl..."
}

Tier missing from JSON → "not offered" → checkout returns 422.

4. Backend .env

STRIPE_API_KEY=sk_test_xxxxxxxxxxxxx
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxx   # from step 5
LAUNCH_DARKLY_SDK_KEY=sdk-xxxxxxxx

5. Stripe CLI for webhooks

Webhook route: POST /api/credits/stripe_webhook. Empty secret = 503 by design (anti-forgery guard in v1.py).

brew install stripe/stripe-cli/stripe
stripe login
stripe listen --forward-to localhost:8006/api/credits/stripe_webhook

The CLI prints Ready! Your webhook signing secret is whsec_... — paste that into STRIPE_WEBHOOK_SECRET and restart the backend.

6. Run the stack

# T1 — backend
cd autogpt_platform/backend && poetry run app

# T2 — webhook forwarder
stripe listen --forward-to localhost:8006/api/credits/stripe_webhook

# T3 — frontend
cd autogpt_platform/frontend && pnpm dev

7. Test cards

Card Result
4242 4242 4242 4242 success
4000 0000 0000 9995 insufficient funds
4000 0025 0000 3155 3DS required
4000 0000 0000 0341 attach OK, fail on charge

Any future expiry, any CVC.

8. Smoke test for this PR

  1. Login → /settings/billing.
  2. Subscription tab → upgrade tier → Stripe Checkout opens → pay with 4242… → return to success URL → CLI logs webhook hit → tier updates.
  3. Automation Credits tabBalanceCard, AutoRefillCard, UsageCard, TransactionHistoryCard should all populate.
  4. Trigger invoice/refund events to exercise InvoicesCard:
    stripe trigger invoice.paid
    stripe trigger charge.refunded
    stripe trigger customer.subscription.updated

9. Reset between runs

Stripe dashboard → Developers → Test data → Delete all test data, then:

UPDATE "User" SET "stripeCustomerId" = NULL;

Happy to pair on it if anything 503s or the LD flag isn't taking — most common gotcha is the webhook secret mismatch (CLI secret ≠ dashboard endpoint secret).

…ettings/billing

Post-merge adjustments after #12933 introduced NO_TIER as the explicit
"no active subscription" state and added has_active_stripe_subscription +
current_period_end on SubscriptionStatusResponse:

- YourPlanCard: paid-detection now reads has_active_stripe_subscription
  (more robust than tier comparison). NO_TIER renders the "No active
  subscription" state with a "Choose a plan" CTA. Surface the renewal
  date when current_period_end is present. Cancel goes through the
  Stripe billing portal so cancellation flows match #12933.
- TIER_ORDER limited to PRO/MAX/BUSINESS (the launch tiers from the
  pricing doc). BASIC + NO_TIER + ENTERPRISE are reserved internal
  states and never offered as upgrade targets in the settings UI.
- Tests updated for the NO_TIER + has_active_stripe_subscription shape.

Topup redirect target moved from /profile/credits to /settings/billing
so users land on the new billing page after a successful Stripe
Checkout. Page-level effect calls fulfill_checkout, shows a toast, and
strips the query string. Backend success_url, cancel_url, billing
portal return_url, and zero/low-balance email links updated.
@Abhi1992002
Abhi1992002 enabled auto-merge April 30, 2026 09:35
…tact-sales, pending state

YourPlanCard:
- Compare-plans link to https://agpt.co/pricing in the section header
- Three-button layout: Downgrade · Manage · Upgrade (only those that apply
  to the current tier are rendered).
- Upgrade button text now reflects the next tier ("Get Pro", "Upgrade to
  Max", "Talk to sales — Team"). Team route opens TEAM_UPGRADE_URL
  (placeholder agpt.co/contact-sales) in a new tab — no Stripe Checkout
  for contact-sales tiers.
- Downgrade is end-of-period: backend modify_stripe_subscription_for_tier
  schedules a phase change at current_period_end, no charge today.
- Pending state surfaced via SubscriptionStatusResponse.pending_tier:
  - "Cancellation scheduled" badge + "Ends on …" subtext when pending NO_TIER
  - "Downgrade scheduled" badge + "Switches to <Tier> on …" when pending paid→paid
  - Cancel/Upgrade/Downgrade buttons hidden while pending; primary CTA
    becomes "Resume subscription" or "Cancel downgrade", which POSTs the
    current tier back to release the schedule.
- Treat users without an active Stripe sub as NO_TIER for display, even if
  the DB tier defaults to PRO (rate-limit default per schema.prisma). Avoids
  rendering "Pro · No active subscription" simultaneously.
- Distinct Button variants per action to fix same-variant collision noted
  in design review (ghost/outline/secondary/primary).
- Loading spinner on Upgrade / Downgrade / Resume / Cancel-downgrade
  while the mutation is in flight.

PaymentMethodCard:
- Local isOpening state on Open-portal click → loading spinner during the
  cross-origin redirect to billing.stripe.com.

BalanceCard (Add credits):
- Better error surfacing on top-up failure: parse status + body.detail so
  the toast shows the underlying Stripe error (e.g. stale customer ID,
  missing product) instead of a generic "no checkout URL" message.

Tests:
- billing-cards.test.tsx covers PRO/MAX/BUSINESS button matrices, NO_TIER
  empty state, and pending-downgrade UI.
- billing-hooks.test.tsx covers nextTier/previousTier derivation,
  canDowngrade gating around pending changes, onDowngrade dispatch, and
  the MAX-onUpgrade-opens-TEAM_UPGRADE_URL branch.
Abhi1992002 and others added 2 commits April 30, 2026 15:40
Replaces the single Your-Plan card on /settings/billing with the tier-grid
pattern that was already shipping on /profile/credits — three cards (Pro,
Max, Team) side by side, click directly on the target tier to switch.

Why
- Single-card "Upgrade to <next tier>" UX could only step one tier at a
  time and didn't surface what the user was paying for vs what was
  available, so jumping PRO → BUSINESS or seeing the rate-limit gap
  required leaving the page.
- The old credits-page picker already had the working pattern:
  per-tier card, "Current" badge, Upgrade/Downgrade button derived from
  TIER_ORDER position, confirmation dialogs, pending-change banner,
  separate Cancel-subscription link. Reusing that shape on the new page
  matches what the team already validated.

What
- helpers.ts now exports PLAN_TIERS (Pro/Max/Team), TIER_ORDER, and
  formatters: getTierLabel, formatTierCost, formatRelativeMultiplier.
  BASIC and ENTERPRISE stay out of the picker per launch-doc tier list
  (BASIC is reserved future slot; ENTERPRISE is admin-managed).
- useYourPlanCard.ts: rewrite around the old SubscriptionTierSection
  hook. Returns the full subscription object plus changeTier,
  handleTierChange (routes downgrades through a confirm callback,
  upgrades through pendingUpgradeTier dialog state, BUSINESS to the
  contact-sales URL), cancelPendingChange (releases pending schedule),
  and Stripe-portal helpers.
- YourPlanCard.tsx: tier grid + pending-change banner with inline
  Resume / Cancel-downgrade button + bottom row with Manage subscription
  and Cancel subscription link + three confirmation dialogs (downgrade,
  upgrade, replace-pending). Treats users without an active Stripe sub
  as NO_TIER for picker comparisons even if the DB tier defaults to PRO,
  so brand-new users see the "Pick a plan" banner and Pro card as the
  upgrade target.
- ENTERPRISE state shows the admin-managed banner — self-service tier
  changes blocked by the backend anyway, but surface the reason in UI.
- Tests rewritten to assert the new layout: tier-card presence per
  state, Current badge highlight, Upgrade/Downgrade/Talk-to-sales button
  routing, pending banner copy, Pick-a-plan banner for unpaid users,
  hook-level changeTier / handleTierChange / cancelPendingChange paths.
- AutoRefillCard: relax validation from Number.isInteger to Number.isFinite
  so legacy non-whole-dollar amounts (e.g. $7.50 stored as 750 cents) don't
  permanently disable the save button. Math.round on save still produces
  integer cents for the backend.
- YourPlanCard: gate canUpgrade on !isPendingCancel && !isPendingDowngrade
  so a pending cancellation no longer shows Resume + Upgrade simultaneously
  (matches the canDowngrade guard).
- Run prettier on touched files.
- Update billing-hooks fractional test: now expects isValid=true for
  $5.50 since the validator was relaxed from Number.isInteger to
  Number.isFinite (legacy non-whole-dollar amounts must remain editable).
- YourPlanCard badge: use "Inactive" instead of "No active subscription"
  for the no-tier case, so it doesn't duplicate the plan label and break
  findByText queries in billing-cards.test.tsx.
… redirect

Trust subscription_tier from the backend instead of forcing NO_TIER when
no active Stripe subscription — matches the old /profile/credits page so
beta users with the default PRO tier see Pro instead of "Upgrade to Pro".
Also handle Stripe's ?subscription=success|cancelled redirect by toasting
and invalidating the subscription status query, so the new tier shows up
without requiring a manual refresh.
Drop the Number.isInteger guard from useBalanceCard for parity with
useAutoRefillCard — backend stores credits in integer cents and the
hook already rounds on submit, so decimals like $5.50 are valid input
and should not be blocked by the frontend.

Update the matching unit test to expect isValid=true for $5.25.
Patch coverage on the Settings v2 billing PR was sitting at 77.25%,
with the bulk of the uncovered lines living in the page-level redirect
handlers and a few card hooks/dialog branches that no test exercised.

- billing-page: cover the ?topup=success / cancel and
  ?subscription=success / cancelled redirect paths via mocked
  next/navigation; assert router.replace clears the query string.
- billing-cards: extend the BalanceCard dialog test to drive the
  amount input and the Cancel button, exercising the input onChange
  and dialog-close handlers.
- billing-hooks: add useYourPlanCard tests for onResume (active
  resume vs short-circuit) and verify canManagePortal flips true
  once the portal URL resolves.

Local billing folder coverage now: 98.24% / 91.42% / 87.5% / 100%
(was 68.42% / 60% / 87.5% / 66.66%).
Comment thread autogpt_platform/backend/backend/data/credit.py
…ollar rule

Backend top_up_intent rejects amount % 100 != 0, so the dialog must
gate decimals client-side instead of letting the user fail at Stripe
checkout. Re-introduces Number.isInteger guard on useBalanceCard
isValid and updates the matching unit test to expect 5.25 to be
invalid.
@Abhi1992002
Abhi1992002 added this pull request to the merge queue Apr 30, 2026
@github-project-automation github-project-automation Bot moved this from 🆕 Needs initial review to 👍🏼 Mergeable in AutoGPT development kanban Apr 30, 2026
Merged via the queue into dev with commit 44096a7 Apr 30, 2026
@Abhi1992002
Abhi1992002 deleted the abhimanyuyadav/billing-page-for-settings-page branch April 30, 2026 12:29
@github-project-automation github-project-automation Bot moved this from 👍🏼 Mergeable to ✅ Done in AutoGPT development kanban Apr 30, 2026
@github-project-automation github-project-automation Bot moved this to Done in Frontend Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

platform/backend AutoGPT Platform - Back end platform/frontend AutoGPT Platform - Front end size/xl

Projects

Status: ✅ Done
Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants