Skip to content

[Dashboard] Add crypto top-up functionality for account credits #7239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

jnsdls
Copy link
Member

@jnsdls jnsdls commented May 31, 2025

Add Crypto Top-Up Feature for Team Credits

This PR adds a new feature allowing teams to top up their credit balance using cryptocurrency. The implementation includes:

  • New UI component for displaying current credit balance and selecting top-up amounts
  • Backend support for retrieving Stripe customer balance
  • Routing for crypto top-up checkout flow
  • Integration with existing billing pages

Key Changes

  • Added getStripeBalance function to retrieve a customer's current balance
  • Created a dedicated checkout route for crypto top-ups with amount selection
  • Implemented getCryptoTopupUrl to generate checkout URLs for crypto payments
  • Added a new CreditTopupSection component with predefined amount options ($25, $100, $500, $1,000)
  • Updated the billing page to display current credit balance and top-up options

The credit balance is displayed prominently and automatically applies to subscription charges before the default payment method is charged.

Summary by CodeRabbit

  • New Features

    • Added a Credit Balance section to the billing page, allowing users to view their current credit balance and top up credits with predefined amounts.
    • Introduced crypto top-up option for team accounts, with error handling for invalid or failed top-up attempts.
    • Enabled fetching and display of Stripe customer balances with accurate credit calculations.
  • Refactor

    • Billing page reorganized into modular components for improved clarity and maintainability.
    • Removed the old Billing component in favor of new, more focused components.
  • Bug Fixes

    • Improved error handling for invalid top-up amounts and failed top-up requests.

PR-Codex overview

This PR introduces functionality for managing credit balances and crypto top-ups in the billing section. It adds new utilities for fetching Stripe customer data and balance, and integrates a credit balance section in the billing page, enhancing user experience for managing payments.

Detailed summary

  • Added getStripeCustomer and getStripeBalance functions to retrieve customer balance from Stripe.
  • Introduced getCryptoTopupUrl function for generating crypto top-up URLs.
  • Updated CheckoutPage to handle crypto top-ups.
  • Enhanced Page component in billing settings to include a CreditBalanceSection.
  • Created CreditBalanceSection component to display current credit balance and allow top-ups.
  • Added utility functions for formatting currency and handling loading states in the credit balance section.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Copy link

changeset-bot bot commented May 31, 2025

⚠️ No Changeset found

Latest commit: d04a91c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link
Contributor

coderabbitai bot commented May 31, 2025

Walkthrough

The changes introduce new Stripe-related actions for retrieving customer and balance information, add a crypto top-up URL utility, and refactor the billing page to use modular components. A new credit balance section with top-up functionality is implemented, and the legacy Billing component is removed. Checkout logic is updated to support crypto top-ups.

Changes

File(s) Change Summary
apps/dashboard/src/@/actions/stripe-actions.ts Added async functions: getStripeCustomer and getStripeBalance for Stripe customer and balance retrieval.
apps/dashboard/src/app/(app)/(stripe)/checkout/[team_slug]/[sku]/page.tsx Updated CheckoutPage to handle topup SKU, parse amount, and redirect to crypto top-up URL if applicable.
apps/dashboard/src/app/(app)/(stripe)/utils/billing.ts Added getCryptoTopupUrl async function to obtain crypto top-up URLs via API.
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/page.tsx Refactored billing page to use modular components and added credit balance display with async Stripe balance fetch.
apps/dashboard/src/components/settings/Account/Billing/index.ts{x} Removed the entire Billing component and its props interface.
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/credit-balance-section.client.tsx Added new CreditBalanceSection React component for displaying and topping up credit balances.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant BillingPage
    participant CreditBalanceSection
    participant StripeActions
    participant StripeAPI

    User->>BillingPage: Loads billing page
    BillingPage->>CreditBalanceSection: Renders with teamSlug and balancePromise
    CreditBalanceSection->>StripeActions: Calls getStripeBalance(team.stripeCustomerId)
    StripeActions->>StripeAPI: Fetch Stripe customer by ID
    StripeAPI-->>StripeActions: Returns customer object
    StripeActions-->>CreditBalanceSection: Returns customer balance
    CreditBalanceSection-->>User: Displays balance and top-up UI

    User->>CreditBalanceSection: Selects top-up amount and clicks Top Up
    CreditBalanceSection->>BillingPage: Navigates to /checkout/[team_slug]/topup?amount=X
    BillingPage->>CheckoutPage: Loads with team_slug, sku=topup, searchParams.amount
    CheckoutPage->>BillingUtils: Calls getCryptoTopupUrl({ teamSlug, amountUSD })
    BillingUtils->>API: POST /v1/teams/{teamSlug}/checkout/crypto-top-up
    API-->>BillingUtils: Returns { result: url }
    BillingUtils-->>CheckoutPage: Returns crypto top-up URL
    CheckoutPage-->>User: Redirects to crypto top-up URL
Loading

Suggested reviewers

  • joaquim-verges

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4d4ac67 and d04a91c.

📒 Files selected for processing (6)
  • apps/dashboard/src/@/actions/stripe-actions.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(stripe)/checkout/[team_slug]/[sku]/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(stripe)/utils/billing.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/credit-balance-section.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/page.tsx (3 hunks)
  • apps/dashboard/src/components/settings/Account/Billing/index.tsx (0 hunks)
💤 Files with no reviewable changes (1)
  • apps/dashboard/src/components/settings/Account/Billing/index.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/dashboard/src/@/actions/stripe-actions.ts
  • apps/dashboard/src/app/(app)/(stripe)/utils/billing.ts
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/credit-balance-section.client.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/page.tsx
  • apps/dashboard/src/app/(app)/(stripe)/checkout/[team_slug]/[sku]/page.tsx
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: Unit Tests
  • GitHub Check: Lint Packages
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Size
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Analyze (javascript)
✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions github-actions bot added the Dashboard Involves changes to the Dashboard. label May 31, 2025
Copy link
Member Author

jnsdls commented May 31, 2025


How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • merge-queue - adds this PR to the back of the merge queue
  • hotfix - for urgent hot fixes, skip the queue and merge this PR next

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

Copy link

vercel bot commented May 31, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
thirdweb-www ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 4, 2025 5:08am
4 Skipped Deployments
Name Status Preview Comments Updated (UTC)
docs-v2 ⬜️ Skipped (Inspect) Jun 4, 2025 5:08am
login ⬜️ Skipped (Inspect) Jun 4, 2025 5:08am
thirdweb_playground ⬜️ Skipped (Inspect) Jun 4, 2025 5:08am
wallet-ui ⬜️ Skipped (Inspect) Jun 4, 2025 5:08am

@vercel vercel bot temporarily deployed to Preview – wallet-ui May 31, 2025 04:59 Inactive
@jnsdls jnsdls marked this pull request as ready for review May 31, 2025 04:59
@jnsdls jnsdls requested review from a team as code owners May 31, 2025 04:59
@vercel vercel bot temporarily deployed to Preview – thirdweb_playground May 31, 2025 04:59 Inactive
@vercel vercel bot temporarily deployed to Preview – docs-v2 May 31, 2025 04:59 Inactive
@vercel vercel bot temporarily deployed to Preview – login May 31, 2025 04:59 Inactive
@jnsdls jnsdls added the DO NOT MERGE This pull request is still in progress and is not ready to be merged. label May 31, 2025 — with Graphite App
Copy link

codecov bot commented May 31, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 55.57%. Comparing base (3d1c321) to head (d04a91c).
Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #7239   +/-   ##
=======================================
  Coverage   55.57%   55.57%           
=======================================
  Files         909      909           
  Lines       58681    58681           
  Branches     4167     4167           
=======================================
  Hits        32612    32612           
  Misses      25961    25961           
  Partials      108      108           
Flag Coverage Δ
packages 55.57% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jnsdls jnsdls force-pushed the _Dashboard_Add_crypto_top-up_functionality_for_account_credits branch from 907f19a to ce3f201 Compare May 31, 2025 05:02
@vercel vercel bot temporarily deployed to Preview – docs-v2 May 31, 2025 05:02 Inactive
@vercel vercel bot temporarily deployed to Preview – login May 31, 2025 05:02 Inactive
@vercel vercel bot temporarily deployed to Preview – wallet-ui May 31, 2025 05:02 Inactive
@vercel vercel bot temporarily deployed to Preview – thirdweb_playground May 31, 2025 05:02 Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 6

🧹 Nitpick comments (1)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/top-up-section.client.tsx (1)

134-139: Currency formatting function could be extracted to a shared utility.

The formatUsd function is simple and correct, but consider extracting it to a shared utility module for reusability across the application.

-function formatUsd(amount: number) {
-  return amount.toLocaleString("en-US", {
-    style: "currency",
-    currency: "USD",
-  });
-}

Consider moving this to a shared utility file like utils/currency.ts:

export function formatUsd(amount: number): string {
  return amount.toLocaleString("en-US", {
    style: "currency",
    currency: "USD",
  });
}
🛑 Comments failed to post (6)
apps/dashboard/src/@/actions/stripe-actions.ts (2)

67-73: 🛠️ Refactor suggestion

Improve error handling and type safety.

The getStripeBalance function could benefit from better error handling and should handle the case where getStripeCustomer might throw an error.

 export async function getStripeBalance(customerId: string) {
-  const customer = await getStripeCustomer(customerId);
-  if (customer.deleted) {
-    return 0;
-  }
-  return customer.balance;
+  try {
+    const customer = await getStripeCustomer(customerId);
+    if (customer.deleted) {
+      return 0;
+    }
+    return customer.balance || 0;
+  } catch (error) {
+    console.error("Error fetching customer balance:", error);
+    return 0; // Return 0 balance as fallback
+  }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

export async function getStripeBalance(customerId: string) {
  try {
    const customer = await getStripeCustomer(customerId);
    if (customer.deleted) {
      return 0;
    }
    return customer.balance || 0;
  } catch (error) {
    console.error("Error fetching customer balance:", error);
    return 0; // Return 0 balance as fallback
  }
}
🤖 Prompt for AI Agents
In apps/dashboard/src/@/actions/stripe-actions.ts around lines 67 to 73, the
getStripeBalance function lacks error handling for potential exceptions thrown
by getStripeCustomer. Wrap the call to getStripeCustomer in a try-catch block to
handle errors gracefully, and ensure the function returns a default value or
propagates the error appropriately. Additionally, add type checks to confirm the
customer object and its properties exist before accessing them to improve type
safety.

63-65: 🛠️ Refactor suggestion

Add error handling for missing customers.

The getStripeCustomer function lacks error handling for cases where the customer doesn't exist, which could cause unhandled exceptions. Consider implementing similar error handling as seen in getTeamInvoices (lines 49-57).

-async function getStripeCustomer(customerId: string) {
-  return await getStripe().customers.retrieve(customerId);
-}
+async function getStripeCustomer(customerId: string) {
+  try {
+    return await getStripe().customers.retrieve(customerId);
+  } catch (error) {
+    if (
+      error instanceof Stripe.errors.StripeError &&
+      error.message.includes("No such customer")
+    ) {
+      return { deleted: true, balance: 0 };
+    }
+    throw error;
+  }
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

async function getStripeCustomer(customerId: string) {
  try {
    return await getStripe().customers.retrieve(customerId);
  } catch (error) {
    if (
      error instanceof Stripe.errors.StripeError &&
      error.message.includes("No such customer")
    ) {
      return { deleted: true, balance: 0 };
    }
    throw error;
  }
}
🤖 Prompt for AI Agents
In apps/dashboard/src/@/actions/stripe-actions.ts around lines 63 to 65, the
getStripeCustomer function does not handle errors when a customer is missing,
potentially causing unhandled exceptions. Add error handling similar to
getTeamInvoices by catching errors from the Stripe API call, checking if the
error indicates a missing customer, and returning null or an appropriate
fallback value instead of throwing. Ensure other errors are rethrown or handled
properly.
apps/dashboard/src/components/settings/Account/Billing/index.tsx (1)

43-43: 💡 Verification agent

🧩 Analysis chain

Hardcoded balance may not reflect actual credit balance.

The currentBalance={0} appears to be hardcoded rather than using the actual Stripe balance. With the new getStripeBalance function available in stripe-actions.ts, this should ideally display the real balance.

Is this hardcoded value intentional for this component, or should it fetch the actual balance using getStripeBalance?


🏁 Script executed:

#!/bin/bash
# Description: Check if this billing component is used elsewhere or if there's a pattern for balance retrieval

# Search for other usage of CreditTopupSection to see how currentBalance is typically handled
rg -A 3 -B 3 "CreditTopupSection.*currentBalance"

# Search for usage of getStripeBalance to see the pattern
rg -A 5 "getStripeBalance"

Length of output: 2699


🏁 Script executed:

#!/bin/bash
set -e

# Display first 30 lines of the Billing index component to inspect import path
echo "----- index.tsx (first 30 lines) -----"
sed -n '1,30p' apps/dashboard/src/components/settings/Account/Billing/index.tsx

# List all files in the Billing component directory
echo "----- Files in Billing directory -----"
ls apps/dashboard/src/components/settings/Account/Billing

# Locate and display the implementation of CreditTopupSection
file=$(fd CreditTopupSection -t f apps/dashboard/src/components/settings/Account/Billing)
echo "----- Implementation file: $file -----"
sed -n '1,200p' "$file"

Length of output: 1856


🏁 Script executed:

#!/bin/bash
set -e

echo "Searching for the Topup section component file..."
fd top-up-section.client.tsx -t f

echo "Looking for any references to CreditTopupSection across the repo..."
rg -n "CreditTopupSection" --glob '*.ts*'

file=$(fd top-up-section.client.tsx -t f)
if [[ -n "$file" ]]; then
  echo "----- Displaying first 200 lines of $file -----"
  sed -n '1,200p' "$file"
else
  echo "Component file not found; please verify its path manually."
  exit 1
fi

Length of output: 7012


Replace hardcoded balance with real Stripe balance.

The currentBalance={0} in apps/dashboard/src/components/settings/Account/Billing/index.tsx (line 43) should be driven by the customer’s actual Stripe balance rather than a constant zero. You can import and call the existing getStripeBalance function (from @/actions/stripe-actions) using team.stripeCustomerId and pass its result into CreditTopupSection.

—for example—

 import CreditTopupSection from ".../top-up-section.client";
+ import { getStripeBalance } from "@/actions/stripe-actions";
  …
  export const Billing: React.FC<BillingProps> = ({ team, … }) => {
-   return (
-     <CreditTopupSection currentBalance={0} teamSlug={team.slug} />
-   );
+   const [balance, setBalance] = useState<number>(0);
+
+   useEffect(() => {
+     if (team.stripeCustomerId) {
+       getStripeBalance(team.stripeCustomerId).then(setBalance);
+     }
+   }, [team.stripeCustomerId]);
+
+   return (
+     <CreditTopupSection
+       currentBalance={balance}
+       teamSlug={team.slug}
+     />
+   );
  };

This ensures your UI reflects the user’s real credit balance.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/dashboard/src/components/settings/Account/Billing/index.tsx at line 43,
replace the hardcoded currentBalance={0} with the actual Stripe balance. Import
the getStripeBalance function from '@/actions/stripe-actions', call it with
team.stripeCustomerId to fetch the real balance asynchronously, and pass the
retrieved balance as the currentBalance prop to CreditTopupSection. Ensure this
data fetching is done appropriately (e.g., in a useEffect or server-side) so the
UI reflects the user's real credit balance.
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/top-up-section.client.tsx (2)

51-59: 💡 Verification agent

🧩 Analysis chain

Verify currency formatting behavior for edge cases.

The current balance display looks good, but the formatUsd function should handle edge cases properly.


🏁 Script executed:

#!/bin/bash
# Search for other usages of currency formatting in the codebase to ensure consistency
rg -A 3 -B 3 "toLocaleString.*currency.*USD"

Length of output: 46


Let's locate the formatUsd implementation to verify how it handles edge cases (e.g., null/undefined, negatives, zero, large values):


🏁 Script executed:

#!/bin/bash
# Find all references and the definition of formatUsd
rg -n "formatUsd" -C 3

Length of output: 3961


🏁 Script executed:

#!/bin/bash
# Retrieve the full formatUsd function to inspect edge case handling
rg -C 10 "function formatUsd" .

Length of output: 2117


🏁 Script executed:

#!/bin/bash
# Locate currentBalance definition and its type within the component
rg -n "currentBalance" -C 5 apps/dashboard/src/app/\(app\)/team/\[team_slug\]/\(team\)/\~/settings/billing/components/top-up-section.client.tsx

Length of output: 1446


Ensure formatUsd Handles Non-finite Values and Consistent Decimals

The formatUsd helper at
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/top-up-section.client.tsx:134-137
currently does:

function formatUsd(amount: number) {
  return amount.toLocaleString("en-US", {
    style: "currency",
    currency: "USD",
  });
}

While toLocaleString will format positive, zero, and negative numbers correctly, it will render "NaN" if amount is not finite and may omit explicit control over decimal places. To guard edge cases, consider adding:

  • A check for non-finite inputs (e.g. !isFinite(amount)) with a fallback ("$0.00" or an empty state).
  • Explicit minimumFractionDigits: 2 and maximumFractionDigits: 2 to ensure exactly two decimal places.

Suggested update:

function formatUsd(amount: number) {
  if (!isFinite(amount)) return "$0.00";
  return amount.toLocaleString("en-US", {
    style: "currency",
    currency: "USD",
    minimumFractionDigits: 2,
    maximumFractionDigits: 2,
  });
}
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/top-up-section.client.tsx
around lines 134 to 137, the formatUsd function does not handle non-finite
values and lacks explicit decimal formatting. Update formatUsd to check if the
input amount is finite, returning "$0.00" if not, and add minimumFractionDigits
and maximumFractionDigits set to 2 in the toLocaleString options to ensure
consistent two-decimal formatting.

117-127: 💡 Verification agent

🧩 Analysis chain

Consider security implications of opening checkout in new tab.

Opening the checkout in a new tab with target="_blank" without rel="noopener noreferrer" could pose security risks. Next.js Link component handles this automatically, but it's worth verifying.


🏁 Script executed:

#!/bin/bash
# Check if Next.js Link component automatically adds security attributes
rg -A 5 -B 5 "target.*_blank" --type tsx --type ts

Length of output: 80


🏁 Script executed:

#!/bin/bash
# Search for any target="_blank" usage in TSX files
rg 'target="_blank"' -g '*.tsx' -A5 -B5

# Ensure the Link component is imported from Next.js
rg "import .*Link from ['\"]next/link['\"]" -g '*.tsx'

# Look for any rel attributes alongside links
rg 'rel=' -g '*.tsx' -A2 -B2

Length of output: 66732


🏁 Script executed:

#!/bin/bash
# Locate the exact path of top-up-section.client.tsx
find apps/dashboard/src/app -type f | rg 'top-up-section.client.tsx'

# Show how Link is imported in that file
find apps/dashboard/src/app -type f | rg 'top-up-section.client.tsx' -print0 | xargs -0 rg -n '^import Link'

# Show the Link element around target="_blank"
find apps/dashboard/src/app -type f | rg 'top-up-section.client.tsx' -print0 | xargs -0 rg -n 'target="_blank"' -A2 -B2

Length of output: 672


Add missing rel="noopener noreferrer" to the new-tab Link

Next.js’ Link passes props through to the underlying <a>, but it does not automatically inject rel="noopener noreferrer". Without it, your checkout page opened in a new tab can access window.opener.

Please update in
• apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/top-up-section.client.tsx
lines 117–127

Suggested diff:

<Button asChild className="w-full" size="lg">
  <Link
    href={`/checkout/${teamSlug}/topup?amount=${selectedAmount}`}
    prefetch={false}
    target="_blank"
+   rel="noopener noreferrer"
  >
    Top Up Credits
    <ArrowRightIcon className="ml-2 h-4 w-4" />
  </Link>
</Button>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

            <Button asChild className="w-full" size="lg">
              <Link
                href={`/checkout/${teamSlug}/topup?amount=${selectedAmount}`}
                prefetch={false}
                target="_blank"
                rel="noopener noreferrer"
              >
                Top Up Credits
                <ArrowRightIcon className="ml-2 h-4 w-4" />
              </Link>
            </Button>
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/top-up-section.client.tsx
around lines 117 to 127, the Link component opens the checkout page in a new tab
using target="_blank" but is missing the rel="noopener noreferrer" attribute. To
fix this, add rel="noopener noreferrer" to the Link component to prevent the new
page from accessing window.opener and mitigate security risks.
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/page.tsx (1)

78-78: 🛠️ Refactor suggestion

Consider edge case handling for balance calculation.

The balance calculation handles the zero case explicitly, but consider what happens if stripeBalance is undefined or null from the API call.

-        currentBalance={stripeBalance === 0 ? 0 : stripeBalance / -100}
+        currentBalance={stripeBalance ? stripeBalance / -100 : 0}

This change handles potential undefined/null values more robustly.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        currentBalance={stripeBalance ? stripeBalance / -100 : 0}
🤖 Prompt for AI Agents
In
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/page.tsx
at line 78, the currentBalance calculation only explicitly handles zero but does
not account for stripeBalance being undefined or null. Update the expression to
first check if stripeBalance is a valid number before performing the division,
and provide a sensible default (such as 0) if it is undefined or null to avoid
runtime errors and ensure robust handling of API response edge cases.

Copy link
Contributor

github-actions bot commented May 31, 2025

size-limit report 📦

Path Size Loading time (3g) Running time (snapdragon) Total time
thirdweb (esm) 62.53 KB (-0.07% 🔽) 1.3 s (-0.07% 🔽) 188 ms (+136.26% 🔺) 1.5 s
thirdweb (cjs) 345.38 KB (0%) 7 s (0%) 622 ms (+3.21% 🔺) 7.6 s
thirdweb (minimal + tree-shaking) 5.7 KB (0%) 114 ms (0%) 39 ms (+669.64% 🔺) 152 ms
thirdweb/chains (tree-shaking) 531 B (0%) 11 ms (0%) 26 ms (+1623.67% 🔺) 37 ms
thirdweb/react (minimal + tree-shaking) 19.56 KB (0%) 392 ms (0%) 63 ms (+480.18% 🔺) 454 ms

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

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 (1)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/page.tsx (1)

75-80: Simplify balance calculation logic.

The conditional check stripeBalance === 0 ? 0 : stripeBalance / -100 is redundant since 0 / -100 = 0. The division and negation will handle the zero case correctly.

-       currentBalance={stripeBalance === 0 ? 0 : stripeBalance / -100}
+       currentBalance={stripeBalance / -100}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 907f19a and ce3f201.

📒 Files selected for processing (6)
  • apps/dashboard/src/@/actions/stripe-actions.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(stripe)/checkout/[team_slug]/[sku]/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(stripe)/utils/billing.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/top-up-section.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/page.tsx (3 hunks)
  • apps/dashboard/src/components/settings/Account/Billing/index.tsx (0 hunks)
💤 Files with no reviewable changes (1)
  • apps/dashboard/src/components/settings/Account/Billing/index.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/dashboard/src/@/actions/stripe-actions.ts
  • apps/dashboard/src/app/(app)/(stripe)/utils/billing.ts
  • apps/dashboard/src/app/(app)/(stripe)/checkout/[team_slug]/[sku]/page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/top-up-section.client.tsx
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Size
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/page.tsx (3)

1-1: LGTM: New stripe balance action imported.

The import of getStripeBalance aligns with the new credit top-up functionality.


6-11: LGTM: Modular component imports support the refactor.

The imports show a clear move from a monolithic Billing component to modular components (CreditsInfoCard, Coupons, PlanInfoCardClient, CreditTopupSection), which improves maintainability.


65-88: LGTM: Well-structured component composition.

The modular approach with clear separation of concerns between plan info, credit top-up, credits info, and coupons improves code organization and maintainability. The prop passing is clean and follows React best practices.

@jnsdls jnsdls force-pushed the _Dashboard_Add_crypto_top-up_functionality_for_account_credits branch from ce3f201 to 36b32b9 Compare June 4, 2025 04:47
@vercel vercel bot temporarily deployed to Preview – login June 4, 2025 04:47 Inactive
@vercel vercel bot temporarily deployed to Preview – wallet-ui June 4, 2025 04:47 Inactive
@vercel vercel bot temporarily deployed to Preview – docs-v2 June 4, 2025 04:47 Inactive
@vercel vercel bot temporarily deployed to Preview – thirdweb_playground June 4, 2025 04:47 Inactive
@jnsdls jnsdls force-pushed the _Dashboard_Add_crypto_top-up_functionality_for_account_credits branch from 36b32b9 to 4d4ac67 Compare June 4, 2025 04:50
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/top-up-section.client.tsx (2)

20-25: Consider making predefined amounts configurable.

The hard-coded amounts limit flexibility for different markets or customer tiers.

Consider accepting predefined amounts as a prop or loading them from configuration:

+interface PredefinedAmount {
+  value: string;
+  label: string;
+}
+
-const predefinedAmounts = [
-  { value: "25", label: "$25" },
-  { value: "100", label: "$100" },
-  { value: "500", label: "$500" },
-  { value: "1000", label: "$1,000" },
-] as const;
+const defaultPredefinedAmounts: PredefinedAmount[] = [
+  { value: "25", label: "$25" },
+  { value: "100", label: "$100" },
+  { value: "500", label: "$500" },
+  { value: "1000", label: "$1,000" },
+];

interface CreditBalanceSectionProps {
  balancePromise: Promise<number>;
  teamSlug: string;
+  predefinedAmounts?: PredefinedAmount[];
}

116-126: Add loading state for checkout navigation.

Consider adding a loading state when the checkout link is clicked to improve UX.

+  const [isNavigating, setIsNavigating] = useState(false);
+
+  const handleCheckoutClick = () => {
+    setIsNavigating(true);
+    // The navigation will happen via the Link component
+  };

-            <Button asChild className="w-full" size="lg">
+            <Button 
+              asChild 
+              className="w-full" 
+              size="lg"
+              disabled={isNavigating}
+            >
              <Link
                href={`/checkout/${teamSlug}/topup?amount=${selectedAmount}`}
                prefetch={false}
                target="_blank"
+               onClick={handleCheckoutClick}
              >
-               Top Up Credits
+               {isNavigating ? "Redirecting..." : "Top Up Credits"}
                <ArrowRightIcon className="ml-2 h-4 w-4" />
              </Link>
            </Button>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce3f201 and 36b32b9.

📒 Files selected for processing (5)
  • apps/dashboard/src/@/actions/stripe-actions.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(stripe)/checkout/[team_slug]/[sku]/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(stripe)/utils/billing.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/top-up-section.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/page.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/dashboard/src/app/(app)/(stripe)/utils/billing.ts
  • apps/dashboard/src/app/(app)/(stripe)/checkout/[team_slug]/[sku]/page.tsx
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/page.tsx
  • apps/dashboard/src/@/actions/stripe-actions.ts
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: Size
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: Unit Tests
  • GitHub Check: Lint Packages
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: Build Packages
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/top-up-section.client.tsx (1)

241-246: LGTM! Clean utility function.

The formatUsd utility function is well-implemented and handles currency formatting correctly.

@vercel vercel bot temporarily deployed to Preview – docs-v2 June 4, 2025 04:50 Inactive
@vercel vercel bot temporarily deployed to Preview – thirdweb_playground June 4, 2025 04:50 Inactive
@vercel vercel bot temporarily deployed to Preview – login June 4, 2025 04:50 Inactive
@vercel vercel bot temporarily deployed to Preview – wallet-ui June 4, 2025 04:50 Inactive
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 36b32b9 and 4d4ac67.

📒 Files selected for processing (6)
  • apps/dashboard/src/@/actions/stripe-actions.ts (1 hunks)
  • apps/dashboard/src/app/(app)/(stripe)/checkout/[team_slug]/[sku]/page.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/(stripe)/utils/billing.ts (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/credit-balance-section.client.tsx (1 hunks)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/page.tsx (3 hunks)
  • apps/dashboard/src/components/settings/Account/Billing/index.tsx (0 hunks)
💤 Files with no reviewable changes (1)
  • apps/dashboard/src/components/settings/Account/Billing/index.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/page.tsx
  • apps/dashboard/src/@/actions/stripe-actions.ts
  • apps/dashboard/src/app/(app)/(stripe)/utils/billing.ts
  • apps/dashboard/src/app/(app)/(stripe)/checkout/[team_slug]/[sku]/page.tsx
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: E2E Tests (pnpm, esbuild)
  • GitHub Check: E2E Tests (pnpm, webpack)
  • GitHub Check: E2E Tests (pnpm, vite)
  • GitHub Check: Size
  • GitHub Check: Build Packages
  • GitHub Check: Lint Packages
  • GitHub Check: Unit Tests
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (3)
apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/settings/billing/components/credit-balance-section.client.tsx (3)

27-131: Excellent component structure and React patterns.

The implementation demonstrates good practices:

  • Proper use of Suspense and ErrorBoundary for async operations
  • Well-structured accessibility with radio group and labels
  • Appropriate use of target="_blank" for checkout flow
  • Clean separation of concerns with helper components

133-172: Well-implemented async data handling.

The current balance components properly handle loading and error states with consistent UI patterns and user-friendly messaging.


240-246: Clean utility function implementation.

The USD formatting function correctly uses the standard browser API with proper locale and currency settings.

@jnsdls jnsdls force-pushed the _Dashboard_Add_crypto_top-up_functionality_for_account_credits branch from 4d4ac67 to d04a91c Compare June 4, 2025 04:56
@vercel vercel bot temporarily deployed to Preview – thirdweb_playground June 4, 2025 04:56 Inactive
@vercel vercel bot temporarily deployed to Preview – wallet-ui June 4, 2025 04:56 Inactive
@vercel vercel bot temporarily deployed to Preview – login June 4, 2025 04:56 Inactive
@vercel vercel bot temporarily deployed to Preview – docs-v2 June 4, 2025 04:56 Inactive
@jnsdls jnsdls removed the DO NOT MERGE This pull request is still in progress and is not ready to be merged. label Jun 4, 2025
@jnsdls jnsdls merged commit 4edf1eb into main Jun 4, 2025
23 of 24 checks passed
@jnsdls jnsdls deleted the _Dashboard_Add_crypto_top-up_functionality_for_account_credits branch June 4, 2025 05:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Dashboard Involves changes to the Dashboard.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant