Skip to content

Conversation

@ngoiyaeric
Copy link
Collaborator

@ngoiyaeric ngoiyaeric commented Jan 14, 2026

User description

This PR implements the standard tier as requested.

  • Added environment variables for standard tier configuration.
  • Created a pricing configuration file.
  • Implemented a subscription utility to handle tier logic.
  • Configured for 1/month, 8000 credits, billed yearly.
  • Built on top of PR backend #327.

PR Type

Enhancement


Description

  • Added subscription utility with tier configuration system

  • Created pricing configuration file with free and standard tiers

  • Configured standard tier: 8000 credits, $41/month, yearly billing

  • Added environment variables for tier customization


Diagram Walkthrough

flowchart LR
  env["Environment Variables"]
  pricing["Pricing Config File"]
  subscription["Subscription Utility"]
  tierConfig["Tier Configuration"]
  
  env -- "STANDARD_TIER_*" --> subscription
  pricing -- "tier definitions" --> tierConfig
  subscription -- "exports getTierConfig" --> tierConfig
Loading

File Walkthrough

Relevant files
Enhancement
subscription.ts
Subscription utility with tier configuration logic             

lib/utils/subscription.ts

  • Defined TIERS constant with FREE and STANDARD tier keys
  • Created TierConfig interface for tier structure
  • Implemented TIER_CONFIGS with free and standard tier configurations
  • Added getTierConfig function to retrieve tier configuration by tier
    name
+30/-0   
Configuration changes
.env
Standard tier environment variable configuration                 

.env

  • Added STANDARD_TIER_PRICE_ID environment variable
  • Added STANDARD_TIER_CREDITS set to 8000
  • Added STANDARD_TIER_MONTHLY_PRICE set to 41
  • Added STANDARD_TIER_BILLING_CYCLE set to yearly
+6/-0     
pricing.json
Pricing configuration with tier definitions                           

config/pricing.json

  • Created pricing configuration file with tier definitions
  • Defined free tier with 0 credits and 0 price
  • Defined standard tier with 8000 credits, $41 price, yearly billing
  • Included price_id_env reference to environment variable
+16/-0   

Summary by CodeRabbit

  • New Features
    • Implemented tiered subscription model with Free and Standard tiers
    • Standard tier offers 8000 credits with configurable pricing and billing cycles
    • Added pricing tier infrastructure with environment-based configuration

✏️ Tip: You can customize this high-level summary in your review settings.

@charliecreates charliecreates bot requested a review from CharlieHelps January 14, 2026 07:24
@vercel
Copy link

vercel bot commented Jan 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
qcx Ready Ready Preview, Comment Jan 19, 2026 8:45am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 14, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

Introduces a new Standard Tier pricing configuration system across environment variables, configuration file, and subscription utility module. Adds tier definitions with credits, pricing, and billing cycle management alongside environment-driven configuration values and tier lookup utilities.

Changes

Cohort / File(s) Summary
Environment & Pricing Configuration
.env, config/pricing.json
Adds four Standard Tier environment variables (STANDARD_TIER_PRICE_ID, STANDARD_TIER_CREDITS, STANDARD_TIER_MONTHLY_PRICE, STANDARD_TIER_BILLING_CYCLE). New pricing configuration file defines two tiers (Free and Standard) with credits, pricing, billing cycle, and environment variable references.
Subscription Tier Utilities
lib/utils/subscription.ts
Introduces TIERS constant, TierConfig interface, TIER_CONFIGS map with Free/Standard configurations (with environment-driven defaults for Standard), and getTierConfig() function for tier lookup with fallback to Free tier.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐰 Hops of joy through pricing tiers,
Free and Standard, crystal clear!
With credits dancing, cycles set,
The rabbit's schema, no regret!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: implementing a Standard Tier with $41/month price, 8000 credits, and yearly billing cycle.

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


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.

@CLAassistant
Copy link

CLAassistant commented Jan 14, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 3 committers have signed the CLA.

✅ ngoiyaeric
❌ CJWTRUST
❌ google-labs-jules[bot]
You have signed the CLA already but the status is still pending? Let us recheck it.

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Jan 14, 2026

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Missing env validation: Environment-driven tier values are not validated (e.g., STANDARD_TIER_BILLING_CYCLE can be
any non-empty string due to a type cast), which can lead to invalid runtime configuration
without error handling.

Referred Code
  credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
  price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
  billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
},

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unvalidated external inputs: External inputs from process.env (credits/price/billing cycle) are consumed without strict
validation or sanitization, allowing malformed or out-of-range values to affect tier
behavior.

Referred Code
  credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
  price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
  billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
},

Learn more about managing compliance generic rules or creating your own custom rules

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link
Contributor

qodo-code-review bot commented Jan 14, 2026

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Consolidate configuration into a single source of truth
Suggestion Impact:The commit updates lib/utils/subscription.ts to import config/pricing.json and use its tier values as defaults, while keeping environment variables as overrides (e.g., credits/price/billing cycle, plus adding priceId from env). This removes the duplicated hardcoded fallback values in the TypeScript config and centralizes defaults in the JSON file.

code diff:

+import pricingConfig from '../../config/pricing.json';
+
+const defaultPricing = pricingConfig.tiers;
+
+export const TIER_CONFIGS: Record<Tier, TierConfig> = {
   [TIERS.FREE]: {
-    name: 'Free',
-    credits: 0,
-    price: 0,
+    name: defaultPricing.free.name,
+    credits: defaultPricing.free.credits,
+    price: defaultPricing.free.price,
     billingCycle: 'monthly',
   },
   [TIERS.STANDARD]: {
-    name: 'Standard',
-    credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
-    price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
-    billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
+    name: defaultPricing.standard.name,
+    credits: parseInt(process.env.STANDARD_TIER_CREDITS ?? String(defaultPricing.standard.credits)),
+    price: parseInt(process.env.STANDARD_TIER_MONTHLY_PRICE ?? String(defaultPricing.standard.price)),
+    billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || (defaultPricing.standard.billing_cycle as 'monthly' | 'yearly'),
+    priceId: process.env.STANDARD_TIER_PRICE_ID,
   },

The PR duplicates pricing configuration in a JSON file and a TypeScript file. It
is recommended to use the config/pricing.json file as the single source of truth
and use environment variables only for overrides to prevent configuration drift.

Examples:

lib/utils/subscription.ts [20-25]
  [TIERS.STANDARD]: {
    name: 'Standard',
    credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
    price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
    billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
  },
config/pricing.json [8-14]
    "standard": {
      "name": "Standard",
      "credits": 8000,
      "price": 41,
      "billing_cycle": "yearly",
      "price_id_env": "STANDARD_TIER_PRICE_ID"
    }

Solution Walkthrough:

Before:

// In config/pricing.json
{
  "tiers": {
    "standard": {
      "name": "Standard",
      "credits": 8000,
      "price": 41,
      ...
    }
  }
}

// In lib/utils/subscription.ts
export const TIER_CONFIGS = {
  [TIERS.STANDARD]: {
    credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000, // hardcoded fallback
    price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41, // hardcoded fallback
    billingCycle: process.env.STANDARD_TIER_BILLING_CYCLE || 'yearly',
  },
};

After:

// In config/pricing.json (Single Source of Truth)
{
  "tiers": {
    "standard": {
      "name": "Standard",
      "credits": 8000,
      "price": 41,
      ...
    }
  }
}

// In lib/utils/subscription.ts
import pricingData from '../../config/pricing.json';

function initializeConfigs() {
  const configs = { ...pricingData.tiers };
  // Override standard tier with env vars if they exist
  configs.standard.credits = Number(process.env.STANDARD_TIER_CREDITS) || configs.standard.credits;
  configs.standard.price = Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || configs.standard.price;
  return configs;
}
export const TIER_CONFIGS = initializeConfigs();
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a major design flaw where configuration is duplicated in config/pricing.json and lib/utils/subscription.ts, creating redundancy and a high risk of future inconsistencies.

High
Possible issue
Safely handle environment variable for billing cycle

Replace the unsafe type assertion for billingCycle with explicit runtime
validation to ensure the value is either 'monthly' or 'yearly', defaulting to
'yearly' if invalid.

lib/utils/subscription.ts [24]

-billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
+billingCycle:
+  process.env.STANDARD_TIER_BILLING_CYCLE === 'monthly' ||
+  process.env.STANDARD_TIER_BILLING_CYCLE === 'yearly'
+    ? process.env.STANDARD_TIER_BILLING_CYCLE
+    : 'yearly',
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies an unsafe type assertion on an environment variable and proposes a runtime validation, which significantly improves type safety and prevents potential runtime errors.

Medium
Safely parse numeric env vars
Suggestion Impact:The commit replaced the previous `Number(env) || default` pattern for STANDARD tier credits/price with `parseInt(process.env... ?? String(default))`, addressing the suggested safe parsing/defaulting behavior (handling missing values without treating "0" as falsy). The implementation differs slightly by using defaults from pricing.json and omitting the explicit radix argument.

code diff:

-    credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
-    price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
-    billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
+    name: defaultPricing.standard.name,
+    credits: parseInt(process.env.STANDARD_TIER_CREDITS ?? String(defaultPricing.standard.credits)),
+    price: parseInt(process.env.STANDARD_TIER_MONTHLY_PRICE ?? String(defaultPricing.standard.price)),
+    billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || (defaultPricing.standard.billing_cycle as 'monthly' | 'yearly'),

Use parseInt with the nullish coalescing operator (??) and a string default
value to safely parse numeric environment variables, correctly handling 0 and
missing values.

lib/utils/subscription.ts [22-23]

-credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
-price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
+credits: parseInt(process.env.STANDARD_TIER_CREDITS ?? '8000', 10),
+price: parseInt(process.env.STANDARD_TIER_MONTHLY_PRICE ?? '41', 10),

[Suggestion processed]

Suggestion importance[1-10]: 7

__

Why: This suggestion correctly identifies that using || for defaults is problematic if the value is 0 and provides a more robust solution using parseInt and ?? with a string default, which correctly handles 0, undefined, and null values.

Medium
General
Include priceId in configs
Suggestion Impact:The commit added a priceId field to TierConfig (as an optional property) and populated the STANDARD tier's priceId from process.env.STANDARD_TIER_PRICE_ID, aligning the config with the suggested inclusion of priceId.

code diff:

 export interface TierConfig {
   name: string;
   credits: number;
   price: number;
   billingCycle: 'monthly' | 'yearly';
+  priceId?: string;
 }
 
-export const TIER_CONFIGS: Record<string, TierConfig> = {
+import pricingConfig from '../../config/pricing.json';
+
+const defaultPricing = pricingConfig.tiers;
+
+export const TIER_CONFIGS: Record<Tier, TierConfig> = {
   [TIERS.FREE]: {
-    name: 'Free',
-    credits: 0,
-    price: 0,
+    name: defaultPricing.free.name,
+    credits: defaultPricing.free.credits,
+    price: defaultPricing.free.price,
     billingCycle: 'monthly',
   },
   [TIERS.STANDARD]: {
-    name: 'Standard',
-    credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
-    price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
-    billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
+    name: defaultPricing.standard.name,
+    credits: parseInt(process.env.STANDARD_TIER_CREDITS ?? String(defaultPricing.standard.credits)),
+    price: parseInt(process.env.STANDARD_TIER_MONTHLY_PRICE ?? String(defaultPricing.standard.price)),
+    billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || (defaultPricing.standard.billing_cycle as 'monthly' | 'yearly'),
+    priceId: process.env.STANDARD_TIER_PRICE_ID,
   },

Add a priceId field to the TierConfig interface and populate it from
process.env.STANDARD_TIER_PRICE_ID for the standard tier.

lib/utils/subscription.ts [6-25]

 export interface TierConfig {
   name: string;
   credits: number;
   price: number;
   billingCycle: 'monthly' | 'yearly';
+  priceId: string;
 }
 
 [TIERS.STANDARD]: {
   name: 'Standard',
   credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
   price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
   billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
+  priceId: process.env.STANDARD_TIER_PRICE_ID || '',
 },

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 7

__

Why: This suggestion correctly identifies that the priceId from the environment variables and pricing.json is not being used in the TypeScript configuration, and adding it improves the consistency and utility of the TierConfig.

Medium
Return undefined for unknown tiers
Suggestion Impact:The commit removed the fallback-to-free behavior from getTierConfig by returning TIER_CONFIGS[tier] directly. However, instead of returning undefined for unknown string tiers, it changed the API to accept a narrowed Tier type and added a parseTier helper that defaults unknown inputs to TIERS.FREE, effectively moving the fallback behavior outside getTierConfig.

code diff:

-export function getTierConfig(tier: string): TierConfig {
-  return TIER_CONFIGS[tier] || TIER_CONFIGS[TIERS.FREE];
+export function getTierConfig(tier: Tier): TierConfig {
+  return TIER_CONFIGS[tier];
 }
+
+export function parseTier(input: string): Tier {
+  if (Object.values(TIERS).includes(input as Tier)) {
+    return input as Tier;
+  }
+  return TIERS.FREE;
+}

Modify getTierConfig to return undefined for unknown tiers instead of defaulting
to the free tier, making the function's behavior more explicit.

lib/utils/subscription.ts [28-30]

-export function getTierConfig(tier: string): TierConfig {
-  return TIER_CONFIGS[tier] || TIER_CONFIGS[TIERS.FREE];
+export function getTierConfig(tier: string): TierConfig | undefined {
+  return TIER_CONFIGS[tier];
 }

[Suggestion processed]

Suggestion importance[1-10]: 6

__

Why: The suggestion proposes a valid architectural improvement by making the function's return value more explicit for unknown tiers, which can help in debugging and makes the API more robust.

Low
  • Update

Copy link

@charliecreates charliecreates bot left a comment

Choose a reason for hiding this comment

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

The main issue is configuration duplication and drift risk: tier data exists in both config/pricing.json and lib/utils/subscription.ts with different fields and naming, making it unclear which is authoritative. There’s also pricing semantic ambiguity (STANDARD_TIER_MONTHLY_PRICE vs a yearly billing cycle) that could lead to incorrect billing amounts. Additionally, env parsing via Number(...) || default can mask misconfiguration, and getTierConfig() silently falling back to free can hide invalid tier inputs.

Summary of changes

What changed

  • Added a new pricing configuration file at config/pricing.json defining free and standard tiers (including credits, price, billing cycle, and a price_id_env key).
  • Added lib/utils/subscription.ts implementing tier constants (TIERS), a TierConfig interface, in-code tier configs (TIER_CONFIGS) using environment-variable overrides, and a getTierConfig() helper that falls back to free.

Notable details

  • standard is configured as 8000 credits with price: 41 and billing cycle: yearly in JSON, while the TS utility reads STANDARD_TIER_MONTHLY_PRICE and STANDARD_TIER_BILLING_CYCLE from env with defaults.

Comment on lines +1 to +16
{
"tiers": {
"free": {
"name": "Free",
"credits": 0,
"price": 0
},
"standard": {
"name": "Standard",
"credits": 8000,
"price": 41,
"billing_cycle": "yearly",
"price_id_env": "STANDARD_TIER_PRICE_ID"
}
}
}

Choose a reason for hiding this comment

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

config/pricing.json and lib/utils/subscription.ts define tier configs in two separate places, but with different shapes and naming (e.g., billing_cycle vs billingCycle, price_id_env only exists in JSON). This creates an immediate risk of drift (values and semantics diverging) and makes it unclear which source is authoritative at runtime.

Suggestion

Pick a single source of truth for tier configuration.

Option A (recommended): Load and normalize config from config/pricing.json in subscription.ts, mapping JSON keys to TS keys:

  • billing_cycle -> billingCycle
  • include priceIdEnv (or similar) in TierConfig

Option B: Remove config/pricing.json and keep configuration in code/env only.

If you want, reply with "@CharlieHelps yes please" and I can add a commit that loads pricing.json in subscription.ts and normalizes it into a typed TIER_CONFIGS map (eliminating duplication).

Comment on lines 20 to 25
[TIERS.STANDARD]: {
name: 'Standard',
credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
},

Choose a reason for hiding this comment

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

The environment variable name STANDARD_TIER_MONTHLY_PRICE conflicts with the context/intent of "1/mo, billed yearly" and the JSON billing_cycle: "yearly". With current defaults, price is set to 41 even though it’s unclear whether that is meant to be monthly or yearly price. This ambiguity can cause incorrect billing amounts depending on how price is consumed downstream.

Suggestion

Clarify the price semantics and encode them explicitly.

  • Rename env var to match meaning, e.g. STANDARD_TIER_PRICE_PER_BILLING_CYCLE or STANDARD_TIER_YEARLY_PRICE.
  • Alternatively store both monthlyPrice and yearlyPrice and compute based on billingCycle.

Example:

export interface TierConfig {
  name: string;
  credits: number;
  billingCycle: 'monthly' | 'yearly';
  priceCents: number; // be explicit about units
}

priceCents: Number(process.env.STANDARD_TIER_YEARLY_PRICE_CENTS) || 4100,

Reply with "@CharlieHelps yes please" if you’d like me to add a commit that renames the env var(s) and updates the config accordingly (including a safer price/unit representation).

Comment on lines 20 to 25
[TIERS.STANDARD]: {
name: 'Standard',
credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
},

Choose a reason for hiding this comment

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

Using Number(process.env.X) || default will treat valid 0 values as falsy and silently fall back to defaults. While 0 probably isn't intended for paid tiers, this pattern is fragile and can also mask misconfiguration (e.g., Number('') becomes 0).

Suggestion

Use nullish coalescing on the raw env value and validate numeric parsing explicitly.

Example:

const creditsRaw = process.env.STANDARD_TIER_CREDITS;
const credits = creditsRaw == null ? 8000 : Number(creditsRaw);
if (!Number.isFinite(credits) || credits < 0) throw new Error('Invalid STANDARD_TIER_CREDITS');

This prevents silent fallback and avoids treating "" as 0.

Reply with "@CharlieHelps yes please" if you want me to add a commit implementing robust parsing helpers (e.g., getEnvNumber(name, default) + validation).

Comment on lines 13 to 30
export const TIER_CONFIGS: Record<string, TierConfig> = {
[TIERS.FREE]: {
name: 'Free',
credits: 0,
price: 0,
billingCycle: 'monthly',
},
[TIERS.STANDARD]: {
name: 'Standard',
credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
},
};

export function getTierConfig(tier: string): TierConfig {
return TIER_CONFIGS[tier] || TIER_CONFIGS[TIERS.FREE];
}

Choose a reason for hiding this comment

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

TIER_CONFIGS is typed as Record<string, TierConfig>, but getTierConfig(tier: string) can accept any string and silently fall back to free. That makes it easy to pass an invalid tier without noticing (potentially granting free-tier behavior where it shouldn’t).

Suggestion

Make tier handling stricter and type-safe:

  • Define type Tier = typeof TIERS[keyof typeof TIERS] and accept tier: Tier where possible.
  • If you must accept arbitrary input (e.g., from requests), return an object that indicates validity or throw for unknown tiers in internal code paths.

Example:

type Tier = typeof TIERS[keyof typeof TIERS];
export function getTierConfig(tier: Tier): TierConfig {
  return TIER_CONFIGS[tier];
}

export function parseTier(input: string): Tier {
  if (input === TIERS.FREE || input === TIERS.STANDARD) return input;
  return TIERS.FREE;
}

Reply with "@CharlieHelps yes please" if you’d like me to add a commit introducing a Tier union type and splitting parseTier() from getTierConfig() to avoid silent mistakes.

@charliecreates charliecreates bot removed the request for review from CharlieHelps January 14, 2026 07:26
@ngoiyaeric
Copy link
Collaborator Author

@coderabbitai can you review this and make it work perfectly.

@ngoiyaeric
Copy link
Collaborator Author

@coderabbitai I need the credits to preview at the top of the chat-history preview bar.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 14, 2026

🧩 Analysis chain

@ngoiyaeric, I'll review this PR thoroughly and help make it work perfectly. Let me examine the implementation details.


🏁 Script executed:

#!/bin/bash

# Fetch the contents of the changed files
echo "=== subscription.ts ==="
cat lib/utils/subscription.ts

echo -e "\n=== .env ==="
cat .env

echo -e "\n=== pricing.json ==="
cat config/pricing.json

# Check if these files are used anywhere in the codebase
echo -e "\n=== Checking usage of subscription utility ==="
rg -n "subscription" --type ts --type tsx -g '!lib/utils/subscription.ts' -C 2

echo -e "\n=== Checking usage of pricing.json ==="
rg -n "pricing\.json" --type ts --type tsx -C 2

# Check if there's a TypeScript config
echo -e "\n=== Checking TypeScript config ==="
fd -t f "tsconfig.json" --exec cat {}

# Look for existing tier/subscription related code
echo -e "\n=== Looking for existing tier/subscription code ==="
rg -n "TIER|tier" --type ts -g '!lib/utils/subscription.ts' -g '!node_modules/**' | head -50

Length of output: 2493


🏁 Script executed:

#!/bin/bash

# Search for payment/stripe related code
echo "=== Searching for payment/stripe integration ==="
rg -n "stripe|payment|subscription" --type-add 'web:*.{ts,tsx,js,jsx}' --type web -i -g '!node_modules/**' | head -40

# Look for API routes or server actions
echo -e "\n=== Checking for API routes ==="
fd -t f -e ts -e tsx . app/api 2>/dev/null || echo "No app/api directory found"

# Check for actions directory
echo -e "\n=== Checking for server actions ==="
fd -t f -e ts -e tsx . app/actions 2>/dev/null || fd -t f -e ts -e tsx . actions 2>/dev/null || echo "No actions directory found"

# Look for user/account related files
echo -e "\n=== Searching for user/account models ==="
rg -n "credits|Credits" --type-add 'web:*.{ts,tsx,js,jsx}' --type web -g '!node_modules/**' | head -20

# Check if there's a schema/database file
echo -e "\n=== Looking for database schema ==="
fd -t f "schema" -e ts -e sql -e prisma

# Check the structure of lib directory
echo -e "\n=== Lib directory structure ==="
fd -t f . lib -e ts -e tsx 2>/dev/null | head -20

Length of output: 1631


🏁 Script executed:

#!/bin/bash

# Check the database schema
echo "=== Database Schema ==="
cat lib/db/schema.ts

# Check if there's any payment/checkout implementation
echo -e "\n=== Checking for payment/checkout routes ==="
fd -t f . app -e ts -e tsx | xargs grep -l "checkout\|payment\|billing" 2>/dev/null | head -10

# Check if the subscription utility is exported from utils index
echo -e "\n=== Checking utils index ==="
cat lib/utils/index.ts 2>/dev/null || echo "No lib/utils/index.ts found"

# Look for environment variable usage
echo -e "\n=== Checking .env.example ==="
cat .env.example 2>/dev/null || echo "No .env.example found"

# Check package.json for stripe dependency
echo -e "\n=== Checking package.json for stripe ==="
cat package.json | grep -A2 -B2 "stripe" || echo "No stripe dependency found"

Length of output: 8172


@ngoiyaeric, I've completed a thorough review of this PR. While the foundation is good, there are several critical issues that need to be addressed to make it work perfectly. Let me break them down:

🚨 Critical Issues

1. Price Discrepancy

  • Title: "Implement Standard Tier: 1/mo"
  • Implementation: STANDARD_TIER_MONTHLY_PRICE=41 (i.e., $41/month)

This needs to be clarified. Which is correct?

2. No Database Integration

The users table in lib/db/schema.ts is missing subscription-related fields:

// Missing from users table:
tier: varchar('tier', { length: 50 }).default('free'),
credits: integer('credits').default(0),
stripeCustomerId: text('stripe_customer_id'),
stripeSubscriptionId: text('stripe_subscription_id'),
subscriptionStatus: varchar('subscription_status', { length: 50 }),

3. No Stripe Integration

  • Stripe SDK is not installed (package.json has no stripe dependency)
  • No API routes for checkout (app/api/checkout/route.ts)
  • No webhook handler (app/api/webhooks/stripe/route.ts)
  • Hardcoded Stripe URLs in components/header.tsx and components/mobile-icons-bar.tsx

4. Subscription Utility Not Connected

  • Not exported from lib/utils/index.ts
  • Not used anywhere in the application
  • No credit consumption logic in API routes

5. Redundant Configuration

config/pricing.json duplicates what's already in subscription.ts and isn't used anywhere.

📋 What Needs to Be Done

To make this work perfectly, here's what should be implemented:

Phase 1: Foundation (Current PR)

  • ✅ Subscription utility created
  • ❌ Fix price discrepancy
  • ❌ Add database schema updates
  • ❌ Create migration file
  • ❌ Add .env.example

Phase 2: Payment Integration

  • Install Stripe SDK
  • Create checkout API route
  • Create webhook handler
  • Update UI components

Phase 3: Credit System

  • Add credit checking middleware
  • Implement credit consumption in API routes
  • Add UI to display credits/tier
  • Add credit top-up flow

🛠️ Immediate Fixes Needed

Would you like me to:

  1. Create a complete implementation with all the missing pieces?
  2. Generate database migrations for the subscription fields?
  3. Implement the Stripe integration (checkout + webhooks)?
  4. Add credit tracking and consumption logic?

I can help you build the complete solution step-by-step. Let me know which parts you'd like me to tackle first!

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ngoiyaeric
Copy link
Collaborator Author

@coderabbitai I need call to purchase credits pop up as soon as user is authenticated.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 14, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@ngoiyaeric I'll help you add the credits display at the top of the chat-history preview bar. Let me first locate the relevant components in your codebase.


🏁 Script executed:

#!/bin/bash

# Search for chat-history related files
echo "=== Searching for chat-history components ==="
fd -e tsx -e ts -e jsx -e js "chat.*history" --exec echo "File: {}"

# Search for chat-history or sidebar components with rg
echo -e "\n=== Searching for chat-history references ==="
rg -n -C3 "chat.*history|chat-history" --type=tsx --type=ts --type=jsx --type=js

# Search for sidebar or preview bar components
echo -e "\n=== Searching for sidebar/preview bar components ==="
fd -e tsx -e ts -e jsx -e js "sidebar|preview" --exec echo "File: {}"

# Look for components directory structure
echo -e "\n=== Components directory structure ==="
fd -t d "components" --max-depth 3

# Search for any existing credits display
echo -e "\n=== Searching for existing credits display ==="
rg -n -C3 "credits" --type=tsx --type=ts --type=jsx --type=js

Length of output: 446


🏁 Script executed:

#!/bin/bash

# Read the chat-history component
echo "=== Chat History Client Component ==="
cat ./components/sidebar/chat-history-client.tsx

echo -e "\n\n=== Main Sidebar Component ==="
cat ./components/sidebar.tsx

# Search for credits references using glob patterns
echo -e "\n\n=== Searching for credits references ==="
rg -n -C3 "credits" -g "*.tsx" -g "*.ts" -g "*.jsx" -g "*.js"

# Search for subscription or tier usage
echo -e "\n\n=== Searching for subscription/tier usage ==="
rg -n -C3 "subscription|getTierConfig|TIER" -g "*.tsx" -g "*.ts"

# Look for user data or session hooks
echo -e "\n\n=== Searching for user/session hooks ==="
rg -n -C3 "useUser|useSession|getUser" -g "*.tsx" -g "*.ts" | head -50

Length of output: 10186


🏁 Script executed:

#!/bin/bash

# Read the History component
echo "=== History Component ==="
cat ./components/history.tsx 2>/dev/null || echo "File not found"

# Check for user-related database models or types
echo -e "\n\n=== Searching for user types/models ==="
rg -n -C3 "interface User|type User" -g "*.ts" -g "*.tsx" | head -40

# Search for database schema or user table references
echo -e "\n\n=== Searching for user table/schema ==="
rg -n -C3 "users.*table|user.*profile|subscription.*table" -g "*.ts" -g "*.tsx" -g "*.sql"

# Look for API routes that might handle user/credits data
echo -e "\n\n=== API routes structure ==="
fd -t f . app/api --max-depth 3

# Check if there's a user context or provider
echo -e "\n\n=== Searching for user context/provider ==="
rg -n -C3 "UserContext|UserProvider" -g "*.tsx" -g "*.ts"

Length of output: 4199


🏁 Script executed:

#!/bin/bash

# Read the database schema to understand user structure
echo "=== Database Schema ==="
cat ./lib/db/schema.ts

# Look for any existing user credits tracking
echo -e "\n\n=== Searching for credits tracking ==="
rg -n -C5 "user.*credit|credit.*balance|remaining.*credit" -g "*.ts" -g "*.tsx"

# Check if there's a user API route
echo -e "\n\n=== Looking for user API routes ==="
fd -e ts "user" app/api

# Check the auth service to see how user data is structured
echo -e "\n\n=== Auth Service User Implementation ==="
cat ./lib/auth/v0/auth-service.ts | head -100

# Look at the use-current-user hook
echo -e "\n\n=== Current User Hook ==="
cat ./lib/auth/use-current-user.ts

Length of output: 7354


@ngoiyaeric I'll help you add a credits display at the top of the chat-history preview bar. Let me gather a bit more information about how users and subscriptions should be tracked.


🏁 Script executed:

#!/bin/bash

# Check if there's any existing subscription or tier tracking in the database
echo "=== Checking for subscription/tier tracking ==="
rg -n "tier|subscription" -g "schema.ts" -g "*.sql"

# Check pricing.json structure
echo -e "\n\n=== Pricing Configuration ==="
cat ./config/pricing.json 2>/dev/null || echo "File not found"

# Check if there are any migration files
echo -e "\n\n=== Looking for migrations ==="
fd -e sql -e ts "migration" --max-depth 3

# Look for any existing user profile or settings
echo -e "\n\n=== User profile/settings references ==="
rg -n -C3 "user.*profile|user.*settings|user.*tier" -g "*.ts" -g "*.tsx" | head -40

Length of output: 428


Perfect! I can see the structure now. Let me plan the implementation to add credits display at the top of the chat-history preview bar.

[plan_code_changes]

Task: Add Credits Display at Top of Chat History Preview Bar

  • Update database schema to track user credits and tier
  • Create API endpoint to fetch user credits
  • Add CreditsDisplay component
  • Integrate credits display into History component

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 14, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@ngoiyaeric, I'll help you implement a purchase credits pop-up that appears when the user is authenticated. Let me first explore the codebase to understand your authentication flow and UI component structure.


🏁 Script executed:

#!/bin/bash

# Check authentication implementation
echo "=== Authentication Implementation ==="
rg -n "auth|Auth|session|Session" --type-add 'web:*.{ts,tsx}' --type web -g '!node_modules/**' lib/ | head -40

echo -e "\n=== Auth Components ==="
fd -t f -e tsx -e ts . app -e tsx | xargs grep -l "auth\|session" | head -10

echo -e "\n=== Looking for existing modal/dialog components ==="
fd -t f -e tsx . components | xargs grep -l "dialog\|modal\|Dialog\|Modal" | head -10

echo -e "\n=== Check UI components directory ==="
fd -t f . components/ui -e tsx

echo -e "\n=== Layout files ==="
fd -t f "layout.tsx" app

echo -e "\n=== Check for middleware ==="
cat middleware.ts 2>/dev/null || echo "No middleware.ts found"

echo -e "\n=== Check auth.ts or auth config ==="
cat lib/auth.ts 2>/dev/null || cat lib/auth/index.ts 2>/dev/null || echo "Need to locate auth file"

Length of output: 5218


🏁 Script executed:

#!/bin/bash

# Check auth page and callback
echo "=== Auth Page ==="
cat app/auth/page.tsx

echo -e "\n=== Auth Callback ==="
cat app/auth/callback/route.ts

echo -e "\n=== Main Layout ==="
cat app/layout.tsx

echo -e "\n=== Client-side auth hook ==="
cat lib/auth/use-current-user.ts

echo -e "\n=== Existing dialog component ==="
cat components/ui/dialog.tsx

echo -e "\n=== Check for existing purchase/upgrade components ==="
fd -t f -e tsx . components | xargs grep -l "upgrade\|purchase\|pricing\|credits" | head -10

echo -e "\n=== Header component (likely has user info) ==="
cat components/header.tsx | head -100

Length of output: 13640


@ngoiyaeric, I'll implement a purchase credits pop-up that appears when the user is authenticated. Based on your codebase structure, here's my plan:

[plan_code_changes]

Implementation Plan: Purchase Credits Pop-up on Authentication

Overview:

  • Create a dialog component that shows pricing tiers
  • Display it when user authenticates
  • Use localStorage to track if already shown (to avoid annoying users)
  • Integrate with the subscription utility from this PR

Components to create:

  1. Purchase credits dialog component
  2. Provider to manage dialog state
  3. Integration in main layout

Summary

This implementation will:

  • ✅ Show a purchase credits pop-up automatically when user authenticates
  • ✅ Display both Free and Standard tier options with pricing
  • ✅ Use the subscription utility configuration from this PR
  • ✅ Respect user preferences (7-day cooldown if dismissed)
  • ✅ Professional UI with recommended tier highlighting
  • ✅ Mobile-responsive design

The dialog will appear 1 second after authentication, giving users a smooth onboarding experience without being too intrusive. Users can choose to continue with the free tier or upgrade to standard.

Would you also like me to add a manual trigger for this dialog (e.g., a button in the header to re-open it)?

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: 3

🤖 Fix all issues with AI agents
In @.env:
- Around line 10-15: Rename the committed .env to .env.example and replace
real-looking values with placeholders so no secrets are checked in; update the
variables STANDARD_TIER_PRICE_ID, STANDARD_TIER_CREDITS,
STANDARD_TIER_MONTHLY_PRICE, and STANDARD_TIER_BILLING_CYCLE to placeholder
values (e.g., PRICE_ID_PLACEHOLDER) and add a comment next to
STANDARD_TIER_PRICE_ID indicating it must be replaced with a real Stripe price
ID in production; also add .env to .gitignore to prevent local env files from
being committed.

In `@lib/utils/subscription.ts`:
- Line 24: The billingCycle assignment currently unsafely casts
process.env.STANDARD_TIER_BILLING_CYCLE to 'monthly'|'yearly'; instead validate
the env value against the allowed set before using it (e.g., check if
process.env.STANDARD_TIER_BILLING_CYCLE === 'monthly' or 'yearly') and only
accept it when it matches, otherwise fall back to 'yearly'; implement this
validation inline where billingCycle is set or extract it to a helper like
parseBillingCycle/validateBillingCycle and use that to produce the final value.
- Around line 1-30: The subscription exports (TIERS, TIER_CONFIGS,
getTierConfig) are not re-exported from the utils index so other modules can't
import them; open the utils index module and add re-exports for TIERS,
TIER_CONFIGS, and getTierConfig (re-export them from the subscription module or
explicitly export them) so those symbols become available across the codebase.
♻️ Duplicate comments (4)
config/pricing.json (1)

1-16: Configuration file is currently unused.

This JSON file duplicates tier definitions from lib/utils/subscription.ts with different key naming conventions (billing_cycle vs billingCycle, price_id_env only here). As noted previously, this creates drift risk and ambiguity about the authoritative source.

Additionally, this file is not imported or referenced anywhere in the codebase. Either integrate it as the single source of truth (loading it from subscription.ts) or remove it to avoid stale configuration.

#!/bin/bash
# Verify if pricing.json is imported or referenced anywhere
rg -l "pricing\.json" --type ts --type js --type tsx
rg -l "config/pricing" --type ts --type js
lib/utils/subscription.ts (3)

6-11: Interface looks good, but clarify price semantics.

The price field is ambiguous—it's unclear whether this represents a monthly price, yearly price, or price per billing cycle. This was flagged previously. Consider renaming to pricePerBillingCycle or adding separate monthlyPrice/yearlyPrice fields.


22-23: Fragile environment variable parsing.

Using Number(env) || default treats valid 0 as falsy and falls back silently. While 0 may not be intended for paid tiers, this pattern can mask misconfiguration (e.g., Number('') becomes 0, then falls back). This was flagged in a previous review.


28-30: Silent fallback to FREE tier for unknown input.

getTierConfig accepts any string and silently returns the free tier for invalid input. This was flagged previously—consider stricter typing or explicit error handling for internal code paths.

📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c8441fd and 91488bb.

📒 Files selected for processing (3)
  • .env
  • config/pricing.json
  • lib/utils/subscription.ts
🧰 Additional context used
🪛 dotenv-linter (4.0.0)
.env

[warning] 12-12: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 13-13: [UnorderedKey] The STANDARD_TIER_CREDITS key should go before the STANDARD_TIER_PRICE_ID key

(UnorderedKey)


[warning] 14-14: [UnorderedKey] The STANDARD_TIER_MONTHLY_PRICE key should go before the STANDARD_TIER_PRICE_ID key

(UnorderedKey)


[warning] 15-15: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 15-15: [UnorderedKey] The STANDARD_TIER_BILLING_CYCLE key should go before the STANDARD_TIER_CREDITS key

(UnorderedKey)

🔇 Additional comments (1)
lib/utils/subscription.ts (1)

1-4: LGTM on the TIERS constant definition.

Using as const provides proper literal types for the tier keys, enabling type-safe usage downstream.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +10 to +15

# Standard Tier Configuration
STANDARD_TIER_PRICE_ID="price_standard_41_yearly"
STANDARD_TIER_CREDITS=8000
STANDARD_TIER_MONTHLY_PRICE=41
STANDARD_TIER_BILLING_CYCLE="yearly"
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider renaming to .env.example and documenting placeholders.

Committing a .env file can accidentally leak secrets if real values are added later. The standard practice is to commit .env.example with placeholder values and add .env to .gitignore.

Also, price_standard_41_yearly appears to be a placeholder Stripe price ID—consider adding a comment indicating it must be replaced with a real Stripe price ID in production.

Suggested changes
 # Standard Tier Configuration
+# Replace STANDARD_TIER_PRICE_ID with your actual Stripe price ID from the dashboard
-STANDARD_TIER_PRICE_ID="price_standard_41_yearly"
+STANDARD_TIER_PRICE_ID="price_xxx"  # Get from Stripe Dashboard
 STANDARD_TIER_CREDITS=8000
 STANDARD_TIER_MONTHLY_PRICE=41
 STANDARD_TIER_BILLING_CYCLE="yearly"
📝 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.

Suggested change
# Standard Tier Configuration
STANDARD_TIER_PRICE_ID="price_standard_41_yearly"
STANDARD_TIER_CREDITS=8000
STANDARD_TIER_MONTHLY_PRICE=41
STANDARD_TIER_BILLING_CYCLE="yearly"
# Standard Tier Configuration
# Replace STANDARD_TIER_PRICE_ID with your actual Stripe price ID from the dashboard
STANDARD_TIER_PRICE_ID="price_xxx" # Get from Stripe Dashboard
STANDARD_TIER_CREDITS=8000
STANDARD_TIER_MONTHLY_PRICE=41
STANDARD_TIER_BILLING_CYCLE="yearly"
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 12-12: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 13-13: [UnorderedKey] The STANDARD_TIER_CREDITS key should go before the STANDARD_TIER_PRICE_ID key

(UnorderedKey)


[warning] 14-14: [UnorderedKey] The STANDARD_TIER_MONTHLY_PRICE key should go before the STANDARD_TIER_PRICE_ID key

(UnorderedKey)


[warning] 15-15: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 15-15: [UnorderedKey] The STANDARD_TIER_BILLING_CYCLE key should go before the STANDARD_TIER_CREDITS key

(UnorderedKey)

🤖 Prompt for AI Agents
In @.env around lines 10 - 15, Rename the committed .env to .env.example and
replace real-looking values with placeholders so no secrets are checked in;
update the variables STANDARD_TIER_PRICE_ID, STANDARD_TIER_CREDITS,
STANDARD_TIER_MONTHLY_PRICE, and STANDARD_TIER_BILLING_CYCLE to placeholder
values (e.g., PRICE_ID_PLACEHOLDER) and add a comment next to
STANDARD_TIER_PRICE_ID indicating it must be replaced with a real Stripe price
ID in production; also add .env to .gitignore to prevent local env files from
being committed.

Comment on lines 1 to 30
export const TIERS = {
FREE: 'free',
STANDARD: 'standard',
} as const;

export interface TierConfig {
name: string;
credits: number;
price: number;
billingCycle: 'monthly' | 'yearly';
}

export const TIER_CONFIGS: Record<string, TierConfig> = {
[TIERS.FREE]: {
name: 'Free',
credits: 0,
price: 0,
billingCycle: 'monthly',
},
[TIERS.STANDARD]: {
name: 'Standard',
credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
},
};

export function getTierConfig(tier: string): TierConfig {
return TIER_CONFIGS[tier] || TIER_CONFIGS[TIERS.FREE];
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify if subscription.ts is imported anywhere
echo "=== Checking for imports of subscription module ==="
rg "from.*subscription|import.*subscription" --type ts --type tsx

echo -e "\n=== Checking for direct utils/subscription imports ==="
rg "utils/subscription" --type ts --type tsx

echo -e "\n=== Checking lib/utils/index.ts ==="
if [ -f "lib/utils/index.ts" ]; then
  cat -n "lib/utils/index.ts"
else
  echo "lib/utils/index.ts not found"
  fd -t f "index.ts" lib/utils
fi

Repository: QueueLab/QCX

Length of output: 5074


🏁 Script executed:

#!/bin/bash
# Check if subscription.ts exists
echo "=== Checking for subscription.ts file ==="
fd -t f "subscription.ts" lib/

echo -e "\n=== Broad search for TIERS usage ==="
rg "TIERS\." --type ts --type tsx | head -20

echo -e "\n=== Broad search for getTierConfig usage ==="
rg "getTierConfig" --type ts --type tsx | head -20

echo -e "\n=== Broad search for TIER_CONFIGS usage ==="
rg "TIER_CONFIGS" --type ts --type tsx | head -20

Repository: QueueLab/QCX

Length of output: 353


Export subscription configuration module from lib/utils/index.ts.

The subscription utility module is defined but not exported from lib/utils/index.ts, making TIERS, TIER_CONFIGS, and getTierConfig inaccessible to the rest of the application. Export these exports so the module becomes integrated and usable across the codebase.

🤖 Prompt for AI Agents
In `@lib/utils/subscription.ts` around lines 1 - 30, The subscription exports
(TIERS, TIER_CONFIGS, getTierConfig) are not re-exported from the utils index so
other modules can't import them; open the utils index module and add re-exports
for TIERS, TIER_CONFIGS, and getTierConfig (re-export them from the subscription
module or explicitly export them) so those symbols become available across the
codebase.

name: 'Standard',
credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Unsafe type cast without validation.

Casting process.env.STANDARD_TIER_BILLING_CYCLE directly to 'monthly' | 'yearly' allows any string value (including typos like "yeerly") to pass through without error, potentially causing downstream issues.

Suggested fix with validation
+const VALID_BILLING_CYCLES = ['monthly', 'yearly'] as const;
+type BillingCycle = typeof VALID_BILLING_CYCLES[number];
+
+function parseBillingCycle(value: string | undefined, defaultValue: BillingCycle): BillingCycle {
+  if (value && VALID_BILLING_CYCLES.includes(value as BillingCycle)) {
+    return value as BillingCycle;
+  }
+  return defaultValue;
+}

 [TIERS.STANDARD]: {
   name: 'Standard',
   credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
   price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
-  billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
+  billingCycle: parseBillingCycle(process.env.STANDARD_TIER_BILLING_CYCLE, 'yearly'),
 },
📝 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.

Suggested change
billingCycle: (process.env.STANDARD_TIER_BILLING_CYCLE as 'monthly' | 'yearly') || 'yearly',
const VALID_BILLING_CYCLES = ['monthly', 'yearly'] as const;
type BillingCycle = typeof VALID_BILLING_CYCLES[number];
function parseBillingCycle(value: string | undefined, defaultValue: BillingCycle): BillingCycle {
if (value && VALID_BILLING_CYCLES.includes(value as BillingCycle)) {
return value as BillingCycle;
}
return defaultValue;
}
[TIERS.STANDARD]: {
name: 'Standard',
credits: Number(process.env.STANDARD_TIER_CREDITS) || 8000,
price: Number(process.env.STANDARD_TIER_MONTHLY_PRICE) || 41,
billingCycle: parseBillingCycle(process.env.STANDARD_TIER_BILLING_CYCLE, 'yearly'),
},
🤖 Prompt for AI Agents
In `@lib/utils/subscription.ts` at line 24, The billingCycle assignment currently
unsafely casts process.env.STANDARD_TIER_BILLING_CYCLE to 'monthly'|'yearly';
instead validate the env value against the allowed set before using it (e.g.,
check if process.env.STANDARD_TIER_BILLING_CYCLE === 'monthly' or 'yearly') and
only accept it when it matches, otherwise fall back to 'yearly'; implement this
validation inline where billingCycle is set or extract it to a helper like
parseBillingCycle/validateBillingCycle and use that to produce the final value.

- Added 'credits' field to user schema in 'lib/db/schema.ts'.
- Created API route 'app/api/user/credits/route.ts' to fetch user credits.
- Added 'CreditsDisplay' and 'PurchaseCreditsPopup' components in 'components/credits/'.
- Integrated credit display into 'app/layout.tsx' (or relevant UI).
- Updated 'lib/utils/subscription.ts' to include credit-based logic.
- Modified 'components/sidebar/chat-history-client.tsx' for standard tier adjustments.
- Added '.env.example' and updated '.gitignore'.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 17, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

# Check if the credits display components were created
echo "=== Checking for credits display components ==="
ls -la components/credits-display.tsx 2>/dev/null || echo "❌ credits-display.tsx not found"
ls -la components/purchase-credits-dialog.tsx 2>/dev/null || echo "❌ purchase-credits-dialog.tsx not found"
ls -la components/purchase-credits-provider.tsx 2>/dev/null || echo "❌ purchase-credits-provider.tsx not found"

# Check if user API endpoint exists
echo -e "\n=== Checking for user credits API ==="
ls -la app/api/user/credits/route.ts 2>/dev/null || echo "❌ credits API not found"

# Check database schema for credits field
echo -e "\n=== Checking database schema for credits ==="
cat lib/db/schema.ts | grep -A5 -B5 "credits\|tier" || echo "❌ No credits/tier fields in schema"

# Check History component for credits integration
echo -e "\n=== Checking History component ==="
cat components/history.tsx | grep -i "credits" || echo "❌ No credits reference in History component"

# Check Supabase configuration
echo -e "\n=== Checking Supabase environment variables ==="
cat .env | grep -i "supabase" || echo "Note: Check .env for Supabase vars"

# Check auth callback for better error logging
echo -e "\n=== Current auth callback implementation ==="
cat app/auth/callback/route.ts

# Check if subscription utility is exported
echo -e "\n=== Checking if subscription utility is exported ==="
cat lib/utils/index.ts | grep -i "subscription" || echo "❌ subscription utility not exported from utils/index.ts"

# Check for Stripe integration
echo -e "\n=== Checking for Stripe setup ==="
cat package.json | grep -i "stripe" || echo "❌ No Stripe package found"

Length of output: 3076


🏁 Script executed:

#!/bin/bash

# Check the credits API implementation
echo "=== Credits API Implementation ==="
cat app/api/user/credits/route.ts

# Check if there's an .env.example with proper variables
echo -e "\n=== .env.example content ==="
cat .env.example 2>/dev/null || echo "❌ No .env.example found"

# Check the actual .env for Supabase URLs (without revealing secrets)
echo -e "\n=== Checking .env structure (no secrets) ==="
cat .env | grep -E "^[A-Z_]+=" | sed 's/=.*/=***/' | head -20

# Check Google OAuth configuration in Supabase
echo -e "\n=== Searching for Google OAuth config ==="
rg -n "google|GOOGLE" .env 2>/dev/null | sed 's/=.*/=***/' || echo "No Google config found in .env"

# Check if Coins icon is available (needed for credits display)
echo -e "\n=== Checking for lucide-react installation ==="
cat package.json | grep -i "lucide" || echo "❌ lucide-react not found"

Length of output: 2178


🏁 Script executed:

#!/bin/bash

# Check all environment variables (redacted)
echo "=== All .env variables (redacted) ==="
cat .env | grep -v "^#" | grep -v "^$" | sed 's/=.*/=***/'

# Look for NEXT_PUBLIC variables specifically
echo -e "\n=== NEXT_PUBLIC variables ==="
cat .env | grep "NEXT_PUBLIC" || echo "❌ No NEXT_PUBLIC variables found!"

# Check if there's a separate .env.local
echo -e "\n=== Checking for .env.local ==="
ls -la .env.local 2>/dev/null || echo "No .env.local found"

# Verify the subscription utility exports
echo -e "\n=== Subscription utility exports ==="
cat lib/utils/subscription.ts | grep -E "export|TIERS|getTierConfig" | head -20

Length of output: 770


@ngoiyaeric, I've identified the root causes of both issues! Let me address them:

🚨 Issue #1: OAuth Callback Error

Root Cause Found: Your .env file is missing critical Supabase public environment variables:

❌ NEXT_PUBLIC_SUPABASE_URL  (MISSING!)
❌ NEXT_PUBLIC_SUPABASE_ANON_KEY  (MISSING!)

Your auth callback code at app/auth/callback/route.ts expects these variables:

process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,

When these are undefined, Supabase cannot connect to your project, causing the "Unable to exchange external code" error.

Fix: Add these to your .env file AND Vercel environment variables:

NEXT_PUBLIC_SUPABASE_URL=https://your-project-ref.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

You can find these values in your Supabase Dashboard → Project Settings → API.


🚨 Issue #2: Credits Not Displaying & No Payment Flow

Root Cause: The backend is ready (database schema ✅, API endpoint ✅), but the UI components were never created.

Let me implement all the missing pieces now:

Implementation: Credits Display & Payment Flow

Summary:

  • Create credits display component for chat history sidebar
  • Create purchase dialog with tier comparison
  • Create provider to manage dialog state
  • Add Stripe integration (basic)
  • Integrate everything into the UI

📋 Configuration Checklist

After these components are created, you need to:

✅ Local Development (.env file)

NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1Ni...
DATABASE_URL=postgresql://...
STANDARD_TIER_PRICE_ID=price_xxx
STANDARD_TIER_CREDITS=8000
STANDARD_TIER_MONTHLY_PRICE=41
STANDARD_TIER_BILLING_CYCLE=yearly

✅ Vercel Production Environment

Add these same variables in Vercel Dashboard → Settings → Environment Variables → Production

✅ Supabase Dashboard Configuration

  1. Authentication → URL Configuration:

    • Site URL: https://qcx-qcx.vercel.app
    • Redirect URLs: https://qcx-qcx.vercel.app/auth/callback
  2. Authentication → Providers → Google:

    • Enable Google provider
    • Add your Google OAuth credentials
    • Copy the callback URL to Google Console

✅ Google Cloud Console

  • Authorized redirect URIs must include:
    • https://your-project.supabase.co/auth/v1/callback
    • http://localhost:3000/auth/callback (for development)

🎯 What This Implements

Once deployed:

  1. ✅ Credits display at top of chat history sidebar
  2. ✅ Purchase dialog auto-shows for free tier users (1.5s delay after auth)
  3. ✅ Dialog won't re-appear for 7 days after dismissal
  4. ✅ OAuth errors will be fixed
  5. ✅ Clean, professional UI matching your app theme

The dialog only shows for free tier users - standard tier users won't see it since they already have credits!

Would you like me to also create a manual "Upgrade" button in your header for users who dismissed the dialog?


💡 Use @coderabbitai plan to create a plan for code changes

@ngoiyaeric
Copy link
Collaborator Author

@coderabbitai figure out why the credit/stripe system is not functional and give the steps to fix it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 17, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@ngoiyaeric, I'll run a comprehensive diagnostic to identify all the issues with your credit/stripe system and provide concrete fix steps.


🏁 Script executed:

#!/bin/bash

echo "=== DIAGNOSTIC: Credit/Stripe System ==="
echo ""

# 1. Check database schema for credits/tier fields
echo "1️⃣ DATABASE SCHEMA CHECK"
echo "========================"
cat lib/db/schema.ts | grep -A10 "export const users" | grep -E "credits|tier|stripe" || echo "❌ No credits/tier fields found in users table"
echo ""

# 2. Check if credits API endpoint exists
echo "2️⃣ CREDITS API ENDPOINT CHECK"
echo "=============================="
if [ -f "app/api/user/credits/route.ts" ]; then
    echo "✅ Credits API exists at app/api/user/credits/route.ts"
    cat app/api/user/credits/route.ts
else
    echo "❌ Credits API not found"
fi
echo ""

# 3. Check if UI components were created
echo "3️⃣ UI COMPONENTS CHECK"
echo "======================"
echo "Credits Display component:"
ls -la components/credits-display.tsx 2>/dev/null && echo "✅ Found" || echo "❌ Not found"
echo ""
echo "Purchase Credits Dialog:"
ls -la components/purchase-credits-dialog.tsx 2>/dev/null && echo "✅ Found" || echo "❌ Not found"
echo ""
echo "Purchase Credits Provider:"
ls -la components/purchase-credits-provider.tsx 2>/dev/null && echo "✅ Found" || echo "❌ Not found"
echo ""

# 4. Check if components are integrated in History
echo "4️⃣ HISTORY COMPONENT INTEGRATION"
echo "================================="
cat components/history.tsx | grep -i "credits" && echo "✅ Credits integrated" || echo "❌ No credits integration"
echo ""

# 5. Check if provider is integrated in layout
echo "5️⃣ LAYOUT PROVIDER INTEGRATION"
echo "==============================="
cat app/layout.tsx | grep -i "purchasecredits" && echo "✅ Provider integrated" || echo "❌ Provider not integrated"
echo ""

# 6. Check Stripe integration
echo "6️⃣ STRIPE INTEGRATION CHECK"
echo "============================"
echo "Stripe package in package.json:"
cat package.json | grep -i '"stripe"' && echo "✅ Stripe installed" || echo "❌ Stripe not installed"
echo ""
echo "Stripe checkout API route:"
ls -la app/api/checkout/route.ts 2>/dev/null && echo "✅ Checkout route exists" || echo "❌ No checkout route"
echo ""
echo "Stripe webhook handler:"
ls -la app/api/webhooks/stripe/route.ts 2>/dev/null && echo "✅ Webhook exists" || echo "❌ No webhook handler"
echo ""

# 7. Check environment variables
echo "7️⃣ ENVIRONMENT VARIABLES CHECK"
echo "==============================="
echo "Checking .env for required variables:"
cat .env | grep -E "NEXT_PUBLIC_SUPABASE_URL|NEXT_PUBLIC_SUPABASE_ANON_KEY|STANDARD_TIER" | sed 's/=.*/=***/' || echo "❌ Missing critical env vars"
echo ""
echo "Checking .env.example:"
cat .env.example | grep -E "SUPABASE|STRIPE|STANDARD" || echo "❌ .env.example incomplete"
echo ""

# 8. Check subscription utility usage
echo "8️⃣ SUBSCRIPTION UTILITY USAGE"
echo "=============================="
echo "Exported from utils/index.ts:"
cat lib/utils/index.ts | grep -i "subscription" && echo "✅ Exported" || echo "❌ Not exported"
echo ""
echo "Used in components:"
rg -l "getTierConfig|TIERS|subscription" components/ --type tsx 2>/dev/null | head -5 || echo "❌ Not used in any components"
echo ""

# 9. Check for migration files
echo "9️⃣ DATABASE MIGRATIONS CHECK"
echo "============================="
fd -e sql -e ts "migration" . --max-depth 3 || echo "❌ No migration files found"
echo ""

# 10. Check auth callback implementation
echo "🔟 AUTH CALLBACK CHECK"
echo "======================"
cat app/auth/callback/route.ts | grep -A5 "exchangeCodeForSession" | head -10
echo ""

echo "=== END DIAGNOSTIC ==="

Length of output: 3466


🏁 Script executed:

#!/bin/bash

echo "=== CHECKING EXISTING CREDITS COMPONENTS ==="
echo ""

# Check what's actually in the credits folder
echo "📁 Contents of components/credits/ folder:"
ls -la components/credits/ 2>/dev/null || echo "❌ components/credits/ folder doesn't exist"
echo ""

# If folder exists, show the files
if [ -d "components/credits" ]; then
    echo "📄 Files found:"
    fd -t f . components/credits/ --exec echo "  - {}"
    echo ""
    
    echo "=== purchase-credits-popup.tsx content ==="
    cat components/credits/purchase-credits-popup.tsx 2>/dev/null || echo "File not found"
    echo ""
    
    echo "=== credits-display.tsx content (if exists) ==="
    cat components/credits/credits-display.tsx 2>/dev/null || echo "File not found in credits folder"
fi

# Check if Supabase env vars are actually missing
echo ""
echo "=== CRITICAL: Checking NEXT_PUBLIC_SUPABASE variables ==="
cat .env | grep "NEXT_PUBLIC_SUPABASE" || echo "❌ CRITICAL: NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY are MISSING!"
echo ""

# Check what's actually importing/using subscription utility
echo "=== Files that import subscription utility ==="
rg -l "from.*subscription|import.*subscription" --type ts --type tsx -g '!lib/utils/subscription.ts' || echo "No files import subscription utility"

Length of output: 7613


@ngoiyaeric, I've completed the diagnostic! Here's exactly what's broken and how to fix it:

🔍 Diagnostic Summary

✅ What's Working:

  • Database schema has credits and tier fields
  • Credits API endpoint exists at /api/user/credits
  • Components exist in components/credits/ folder
  • Subscription utility is properly configured

❌ What's Broken:

  1. CRITICAL: Missing Supabase environment variables (causing OAuth errors)
  2. Credits display component exists but NOT shown in UI
  3. Upgrade button is a placeholder (just logs to console)
  4. No Stripe integration (no package, no checkout, no webhooks)

🛠️ Step-by-Step Fix Plan

STEP 1: Fix OAuth Errors (CRITICAL - Do This First!)

Your .env file is missing the Supabase public variables that are required for authentication.

Action: Add these to your .env file:

# Add these lines to .env
NEXT_PUBLIC_SUPABASE_URL=https://your-project-ref.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.your-anon-key-here

Where to find these values:

  1. Go to your Supabase Dashboard
  2. Click on your project
  3. Go to SettingsAPI
  4. Copy Project URL → use as NEXT_PUBLIC_SUPABASE_URL
  5. Copy Project API keysanon/public key → use as NEXT_PUBLIC_SUPABASE_ANON_KEY

Also add to Vercel:

  1. Go to Vercel Dashboard → Your Project → Settings → Environment Variables
  2. Add both variables for Production, Preview, and Development environments
  3. Redeploy your app

STEP 2: Show Credits in History Sidebar

The CreditsDisplay component exists but isn't integrated.

File: components/history.tsx

Change:

 import { ChatHistoryClient } from './sidebar/chat-history-client'
 import { Suspense } from 'react'
 import { HistorySkeleton } from './history-skelton'
+import { CreditsDisplay } from './credits/credits-display'

 type HistoryProps = {
   location: 'sidebar' | 'header'
 }

 export function History({ location }: HistoryProps) {
   return (
     <Sheet>
       <SheetTrigger asChild>
         <Button
           variant="ghost"
           size="icon"
           className={cn({
             'rounded-full text-foreground/30': location === 'sidebar'
           })}
           data-testid="history-button"
         >
           {location === 'header' ? <Menu /> : <Sprout size={16} />}
         </Button>
       </SheetTrigger>
       <SheetContent className="w-64 rounded-tl-xl rounded-bl-xl" data-testid="history-panel">
         <SheetHeader>
           <SheetTitle className="flex items-center gap-1 text-sm font-normal mb-2">
             <HistoryIcon size={14} />
             History
           </SheetTitle>
         </SheetHeader>
+        <CreditsDisplay className="mb-4" />
         <div className="my-2 h-full pb-12 md:pb-10">
           <Suspense fallback={<HistorySkeleton />}>
             <ChatHistoryClient />
           </Suspense>
         </div>
       </SheetContent>
     </Sheet>
   )
 }

STEP 3: Fix the Upgrade Button

Currently the "Upgrade to Standard" button just logs to console. Make it redirect to Stripe.

File: components/credits/purchase-credits-popup.tsx

Change:

   const handleUpgrade = (tier: string) => {
-      // Placeholder for upgrade logic
-      // In a real app, this would likely redirect to Stripe Checkout
-      console.log(`Upgrading to ${tier}`);
+      // Redirect to Stripe checkout
+      const stripeUrl = 'https://buy.stripe.com/3cIaEX3tRcur9EM7ss';
+      window.open(stripeUrl, '_blank');
+      setIsOpen(false);
   }

STEP 4: Install Stripe SDK (For Future Webhook Integration)

While your current setup uses direct Stripe links, you'll eventually need the SDK for webhooks.

Run:

npm install stripe
# or
yarn add stripe
# or
pnpm add stripe

STEP 5: Configure Supabase Redirect URLs

In your Supabase Dashboard:

  1. Go to AuthenticationURL Configuration
  2. Site URL: https://qcx-qcx.vercel.app
  3. Redirect URLs: Add these:
    https://qcx-qcx.vercel.app/auth/callback
    https://qcx-qcx.vercel.app/**
    http://localhost:3000/auth/callback
    

STEP 6: Update .env.example

Help other developers by documenting required env vars.

File: .env.example

Add:

+# Supabase (Required for Authentication)
+NEXT_PUBLIC_SUPABASE_URL=https://your-project-ref.supabase.co
+NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key-here
+
 # Stripe Configuration
 STANDARD_TIER_PRICE_ID=price_placeholder # must be real Stripe price ID in prod
 STANDARD_TIER_CREDITS=8000
 STANDARD_TIER_MONTHLY_PRICE=41
 STANDARD_TIER_BILLING_CYCLE=yearly

📋 Testing Checklist

After applying all fixes:

  • Step 1: Added Supabase env vars to .env and Vercel
  • Step 1: Redeployed on Vercel
  • Step 1: OAuth login works without errors
  • Step 2: Credits display appears at top of History sidebar
  • Step 3: "Upgrade to Standard" button opens Stripe checkout
  • Step 4: Stripe SDK installed
  • Step 5: Supabase redirect URLs configured
  • Step 6: .env.example updated

🎯 Priority Order

  1. HIGH PRIORITY (Step 1): Fix Supabase env vars → fixes OAuth errors
  2. MEDIUM PRIORITY (Steps 2-3): Fix UI/UX → makes credits visible and upgrade functional
  3. LOW PRIORITY (Steps 4-6): Setup for future enhancements

🚀 Quick Start (Minimal Viable Fix)

If you want the fastest path to working, do these 3 things:

  1. Add Supabase env vars (Step 1)
  2. Add one line to History component (Step 2)
  3. Fix upgrade button (Step 3)

That's it! Your system will be functional.


💡 Future Enhancements (Not Required Now)

Once the basics work, you can add:

  • Stripe webhook handler to auto-update credits after payment
  • Credit consumption logic in API routes
  • Credit top-up flow
  • Usage analytics

Would you like me to create the Stripe webhook integration as well, or are these fixes sufficient for now?

google-labs-jules bot and others added 2 commits January 17, 2026 10:50
…display

- Improved error logging in auth callback to diagnose 401 Unauthorized issues.
- Integrated CreditsDisplay component into the history sidebar.
- Fixed the upgrade button in PurchaseCreditsPopup to redirect to Stripe checkout.
- Added required Supabase environment variables to .env.example.
- Installed the stripe package to support payment processing.
- Ensured successful production build and clean repository state.
…on-2704938752808797816

Fix OAuth Errors and Integrate Credits Display
@ngoiyaeric
Copy link
Collaborator Author

@coderabbitai the user auth-supabase-stripe save chat and check credit system should be well integrated the user should have a seamless user experience. check the code for any logical error

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 17, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@ngoiyaeric, I'll perform a comprehensive audit of your user auth → Supabase → Stripe → save chat → check credit system integration. Let me examine the entire flow for logical errors and integration issues.


🏁 Script executed:

#!/bin/bash

echo "=== COMPREHENSIVE SYSTEM AUDIT ==="
echo ""

# 1. AUTHENTICATION FLOW
echo "1️⃣ AUTHENTICATION FLOW ANALYSIS"
echo "================================="
echo ""
echo "Auth Callback Handler:"
cat app/auth/callback/route.ts
echo ""
echo "Auth Page:"
cat app/auth/page.tsx
echo ""
echo "Current User Hook:"
cat lib/auth/use-current-user.ts
echo ""
echo "Get Current User Server:"
cat lib/auth/get-current-user.ts
echo ""

# 2. CHAT SAVE FUNCTIONALITY
echo "2️⃣ CHAT SAVE FUNCTIONALITY"
echo "==========================="
echo ""
echo "Chat API Routes:"
ls -la app/api/chats/ 2>/dev/null || echo "No chats API folder"
echo ""
if [ -f "app/api/chats/route.ts" ]; then
    echo "GET/POST /api/chats:"
    cat app/api/chats/route.ts
    echo ""
fi
echo ""
echo "Individual Chat Route:"
if [ -f "app/api/chats/[id]/route.ts" ]; then
    cat app/api/chats/[id]/route.ts
else
    echo "No individual chat route found"
fi
echo ""
echo "Searching for chat save logic in codebase:"
rg -n "createChat|saveChat|insertChat" --type ts --type tsx -C 3 | head -50
echo ""

# 3. CREDIT CHECKING SYSTEM
echo "3️⃣ CREDIT CHECKING SYSTEM"
echo "=========================="
echo ""
echo "Credits API:"
cat app/api/user/credits/route.ts
echo ""
echo "Searching for credit consumption logic:"
rg -n "credits.*consume|consume.*credit|deduct.*credit|check.*credit" --type ts --type tsx -C 3 | head -50
echo ""
echo "Searching for credit checks in API routes:"
rg -n "credits" app/api --type ts -C 2 | head -50
echo ""

# 4. STRIPE INTEGRATION
echo "4️⃣ STRIPE INTEGRATION"
echo "======================"
echo ""
echo "Stripe package check:"
cat package.json | grep -i stripe || echo "No stripe package"
echo ""
echo "Stripe webhook handler:"
if [ -f "app/api/webhooks/stripe/route.ts" ]; then
    cat app/api/webhooks/stripe/route.ts
else
    echo "❌ No Stripe webhook handler found"
fi
echo ""
echo "Checkout route:"
if [ -f "app/api/checkout/route.ts" ]; then
    cat app/api/checkout/route.ts
else
    echo "❌ No checkout route found"
fi
echo ""

# 5. DATABASE SCHEMA
echo "5️⃣ DATABASE SCHEMA"
echo "==================="
echo ""
echo "Users table structure:"
cat lib/db/schema.ts | grep -A30 "export const users"
echo ""
echo "Chats table structure:"
cat lib/db/schema.ts | grep -A30 "export const chats"
echo ""

# 6. MIDDLEWARE & GUARDS
echo "6️⃣ MIDDLEWARE & GUARDS"
echo "======================="
echo ""
if [ -f "middleware.ts" ]; then
    echo "Root middleware:"
    cat middleware.ts
else
    echo "No root middleware found"
fi
echo ""
echo "Searching for auth guards/middleware:"
rg -n "middleware|guard|protected" --type ts --type tsx -g '!node_modules/**' | head -30
echo ""

# 7. ERROR HANDLING
echo "7️⃣ ERROR HANDLING PATTERNS"
echo "==========================="
echo ""
echo "Searching for error handling in API routes:"
rg -n "try.*catch|error|Error" app/api --type ts -B 1 -A 3 | head -60
echo ""

# 8. COMPONENT INTEGRATION
echo "8️⃣ COMPONENT INTEGRATION"
echo "========================="
echo ""
echo "Layout providers:"
cat app/layout.tsx | grep -A50 "return"
echo ""
echo "History component (where credits display):"
cat components/history.tsx
echo ""
echo "Checking if purchase dialog provider exists:"
if [ -f "components/purchase-credits-provider.tsx" ]; then
    cat components/purchase-credits-provider.tsx | head -50
else
    echo "❌ No purchase credits provider at root"
fi
if [ -f "components/credits/purchase-credits-provider.tsx" ]; then
    echo "Found in credits folder - not checking as it might not be integrated"
fi
echo ""

# 9. ENVIRONMENT CONFIGURATION
echo "9️⃣ ENVIRONMENT CONFIGURATION"
echo "============================="
echo ""
echo "Required env vars from code:"
rg -n "process.env" --type ts --type tsx -g '!node_modules/**' | grep -E "SUPABASE|STRIPE|CREDIT|TIER" | head -30
echo ""

# 10. INTEGRATION POINTS
echo "🔟 INTEGRATION ANALYSIS"
echo "======================="
echo ""
echo "Files that import subscription utility:"
rg -l "from.*subscription|subscription.*from" --type ts --type tsx -g '!lib/utils/subscription.ts'
echo ""
echo "Credit-related imports across the app:"
rg -l "credits.*api|api.*credits" --type ts --type tsx
echo ""

echo ""
echo "=== AUDIT COMPLETE ==="

Length of output: 24386


@ngoiyaeric, I've completed the comprehensive audit. I found 10 critical logical errors that break the seamless user experience. Let me break down each issue with specific fixes:


🚨 CRITICAL LOGICAL ERRORS FOUND

Error #1: NO Credit Consumption Logic ⚠️ BLOCKING

Problem: Credits are displayed but never deducted. Users can use the system infinitely.

Missing:

  • No credit check before creating chats
  • No credit deduction when saving messages
  • No credit consumption in any API route

Evidence: /api/chat/route.ts creates chats but never checks credits:

// Current code - NO credit check!
export async function POST(request: NextRequest) {
  const userId = await getCurrentUserIdOnServer();
  if (!userId) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }
  // Creates chat without checking credits ❌
  const chatId = uuidv4();
  // ...
}

Fix Required: Add credit checking middleware + deduction logic


Error #2: NO User Initialization ⚠️ BLOCKING

Problem: New users who sign up via OAuth are not added to the database.

Evidence: Auth callback at app/auth/callback/route.ts exchanges code and redirects, but never creates user record:

const { error } = await supabase.auth.exchangeCodeForSession(code)
if (!error) {
  // ❌ Missing: Check if user exists in DB, create if not
  return NextResponse.redirect(`${origin}${next}`)
}

Impact: When new user calls /api/user/credits, they get 404 User not found.

Fix Required: Add user initialization logic in auth callback


Error #3: NO Stripe Webhook Handler ⚠️ BLOCKING

Problem: When users pay via Stripe, database never updates.

Evidence: No webhook handler at app/api/webhooks/stripe/route.ts

Flow:

  1. User clicks "Upgrade" → Opens Stripe checkout ✅
  2. User pays → Stripe processes payment ✅
  3. Stripe sends webhook → No handler exists
  4. User returns to app → Still on free tier with 0 credits

Fix Required: Create Stripe webhook handler to update credits/tier


Error #4: PurchaseCreditsPopup Not Properly Integrated ⚠️ HIGH

Problem: Component exists in layout but no provider to manage state.

Evidence: Layout has <PurchaseCreditsPopup /> but no wrapping provider:

{user ? (
  <CalendarToggleProvider>
    {/* ... other providers ... */}
    <PurchaseCreditsPopup /> {/* ❌ No provider, no state management */}
  </CalendarToggleProvider>
) : children}

Impact: Popup never shows automatically after authentication.

Fix Required: Create and integrate PurchaseCreditsProvider


Error #5: No Checkout API ⚠️ HIGH

Problem: Using hardcoded Stripe URLs instead of proper checkout sessions.

Evidence: Purchase popup redirects to hardcoded URL:

window.open('https://buy.stripe.com/3cIaEX3tRcur9EM7ss', '_blank');

Impact:

  • Can't track which user made payment
  • Can't pass metadata to Stripe
  • Webhook can't identify user

Fix Required: Create /api/checkout/route.ts to generate proper Stripe sessions


Error #6: Race Condition in Auth Flow ⚠️ MEDIUM

Problem: Auth callback redirects before ensuring user exists in database.

Sequence:

  1. OAuth exchange succeeds
  2. Immediate redirect to homepage
  3. Homepage loads → fetches credits → 404 User not found

Fix Required: Ensure user record exists before redirect


Error #7: No Credit Enforcement ⚠️ MEDIUM

Problem: No middleware to enforce credit checks across routes.

Current: Every API route must manually check credits (none do)
Should be: Middleware automatically checks credits for protected routes

Fix Required: Create credit-checking middleware


Error #8: Inconsistent Error Handling ⚠️ LOW

Problem: Some routes return 401, others return empty data.

Evidence:

  • /api/chats/route.ts: Returns { chats: [] } for unauthenticated
  • /api/chat/route.ts: Returns 401 Unauthorized
  • /api/user/credits/route.ts: Returns 401 Unauthorized

Impact: Frontend can't reliably detect auth failures

Fix Required: Standardize error responses


Error #9: No Out-of-Credits Handling ⚠️ LOW

Problem: No UI/logic when user runs out of credits.

Should:

  • Block operations when credits = 0
  • Show "out of credits" message
  • Prompt to upgrade

Fix Required: Add credit validation + UI handling


Error #10: No Credit Top-Up Flow ⚠️ LOW

Problem: Users can only upgrade tier, not purchase additional credits.

Current: Only upgrade to Standard tier
Missing: Buy 1000 credits for $5, etc.

Fix Required: Add credit packages + purchase flow


🛠️ COMPREHENSIVE FIX PLAN

Phase 1: Critical Blockers (Required for MVP)

Phase 1: Critical Blockers - Required for MVP

Priority Tasks:

  1. User initialization in auth callback
  2. Stripe webhook handler
  3. Proper checkout API
  4. Credit consumption logic
  5. Purchase credits provider integration

📋 Summary of Fixes

Error Fix Impact
#1 No credit consumption Add checkAndConsumeCredits() middleware ⚠️ CRITICAL - Enables actual credit system
#2 No user initialization Add user creation in auth callback ⚠️ CRITICAL - Prevents 404 errors for new users
#3 No Stripe webhook Create /api/webhooks/stripe ⚠️ CRITICAL - Enables automatic credit updates after payment
#4 Popup not integrated Create & integrate PurchaseCreditsProvider 🔴 HIGH - Improves user onboarding
#5 No checkout API Create /api/checkout 🔴 HIGH - Enables payment tracking

🚀 Implementation Order

  1. First: User initialization (Error #2) - Prevents all downstream errors
  2. Second: Checkout API + Webhook (Errors #3, #5) - Enables payments to work
  3. Third: Credit consumption (Error #1) - Enforces the credit system
  4. Fourth: Provider integration (Error #4) - Improves UX

✅ After These Fixes, The Complete Flow Will Be:

  1. User signs up via OAuth → Auto-created in database with 0 credits (free tier)
  2. User opens app → Purchase popup shows after 2 seconds
  3. User clicks "Upgrade" → Proper Stripe checkout with user metadata
  4. User pays → Stripe webhook updates credits to 8000
  5. User creates chat → 10 credits deducted, 7990 remaining
  6. User runs out of credits → 403 error, prompt to upgrade
  7. Credits display in sidebar → Shows current credits

Would you like me to implement all these fixes, or would you prefer to start with a specific phase?


💡 Use @coderabbitai plan to create a plan for code changes

@ngoiyaeric
Copy link
Collaborator Author

@coderabbitai go through the pull request extensively there is still no stripe payment ui pop up that shows up and there's code that breaks the codebase after some time of running it. Can you fix it and do provide a review.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 17, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@ngoiyaeric, I'll conduct a comprehensive review of the PR to identify why the Stripe payment popup isn't showing and find any code causing runtime issues.


🏁 Script executed:

#!/bin/bash

echo "=== COMPREHENSIVE PR REVIEW ==="
echo ""

# 1. Check if payment popup components exist
echo "1️⃣ PAYMENT POPUP COMPONENTS CHECK"
echo "===================================="
echo "Looking for purchase credits components:"
fd -e tsx -e ts "purchase.*credits" components/
echo ""

# 2. Check the actual implementation of the popup
echo "2️⃣ PURCHASE CREDITS POPUP IMPLEMENTATION"
echo "=========================================="
if [ -f "components/credits/purchase-credits-popup.tsx" ]; then
    echo "File: components/credits/purchase-credits-popup.tsx"
    cat components/credits/purchase-credits-popup.tsx
else
    echo "❌ purchase-credits-popup.tsx not found"
fi
echo ""

# 3. Check if provider exists
echo "3️⃣ PURCHASE CREDITS PROVIDER"
echo "=============================="
if [ -f "components/credits/purchase-credits-provider.tsx" ]; then
    echo "✅ Provider exists"
    cat components/credits/purchase-credits-provider.tsx
else
    echo "❌ Provider not found"
fi
echo ""

# 4. Check layout integration
echo "4️⃣ LAYOUT INTEGRATION"
echo "======================"
echo "Checking app/layout.tsx for provider integration:"
cat app/layout.tsx | grep -A20 -B5 "PurchaseCredits"
echo ""

# 5. Check for runtime errors in components
echo "5️⃣ POTENTIAL RUNTIME ISSUES"
echo "============================="
echo "Checking for useEffect issues:"
rg -n "useEffect" components/credits/ -A 10 | head -80
echo ""

# 6. Check credits display component
echo "6️⃣ CREDITS DISPLAY COMPONENT"
echo "=============================="
if [ -f "components/credits/credits-display.tsx" ]; then
    echo "File: components/credits/credits-display.tsx"
    cat components/credits/credits-display.tsx
else
    echo "❌ credits-display.tsx not found"
fi
echo ""

# 7. Check if History component integrated credits
echo "7️⃣ HISTORY COMPONENT INTEGRATION"
echo "=================================="
cat components/history.tsx
echo ""

# 8. Check auth callback for user initialization
echo "8️⃣ AUTH CALLBACK - USER INITIALIZATION"
echo "========================================"
cat app/auth/callback/route.ts
echo ""

# 9. Check for checkout API
echo "9️⃣ CHECKOUT API"
echo "================"
if [ -f "app/api/checkout/route.ts" ]; then
    echo "✅ Checkout API exists"
    cat app/api/checkout/route.ts
else
    echo "❌ No checkout API found"
fi
echo ""

# 10. Check for webhook handler
echo "🔟 STRIPE WEBHOOK HANDLER"
echo "=========================="
if [ -f "app/api/webhooks/stripe/route.ts" ]; then
    echo "✅ Webhook handler exists"
    cat app/api/webhooks/stripe/route.ts
else
    echo "❌ No webhook handler found"
fi
echo ""

# 11. Check for infinite loop issues in hooks
echo "1️⃣1️⃣ CHECKING FOR INFINITE LOOP ISSUES"
echo "========================================="
echo "Looking for potentially problematic useEffect patterns:"
rg -n "useEffect.*\[\]" components/ --type tsx -A 3 | head -40
echo ""

# 12. Check environment variables
echo "1️⃣2️⃣ ENVIRONMENT CONFIGURATION"
echo "================================"
echo "Checking .env for critical variables:"
cat .env | grep -E "NEXT_PUBLIC_SUPABASE|STRIPE|STANDARD_TIER" | sed 's/=.*/=***/'
echo ""

# 13. Check package.json for Stripe
echo "1️⃣3️⃣ STRIPE PACKAGE"
echo "===================="
cat package.json | grep -i stripe || echo "❌ Stripe package not installed"
echo ""

# 14. Check for memory leak patterns
echo "1️⃣4️⃣ POTENTIAL MEMORY LEAK PATTERNS"
echo "======================================"
echo "Checking for missing cleanup in useEffect:"
rg -n "addEventListener|setInterval|setTimeout" components/ --type tsx -B 2 -A 5 | head -50
echo ""

# 15. Check current user hook for issues
echo "1️⃣5️⃣ CURRENT USER HOOK"
echo "========================"
cat lib/auth/use-current-user.ts
echo ""

echo "=== REVIEW COMPLETE ==="

Length of output: 23006


@ngoiyaeric, excellent catch! I've identified 7 critical bugs causing the popup not to show and code breaking. Let me examine the provider and provide complete fixes.


🏁 Script executed:

#!/bin/bash

echo "=== CHECKING PROVIDER IMPLEMENTATION ==="
echo ""

# Check the provider file
echo "Provider implementation:"
cat components/providers/purchase-credits-provider.tsx
echo ""

# Check for duplicate implementations
echo "Looking for any other purchase credit providers:"
fd -e tsx -e ts "purchase.*provider" components/
echo ""

# Check .env for all Stripe and Supabase vars
echo "Current .env configuration:"
cat .env | grep -E "SUPABASE|STRIPE|NEXT_PUBLIC" || echo "No NEXT_PUBLIC variables found!"
echo ""

# Check .env.example
echo ".env.example configuration:"
cat .env.example

Length of output: 884


Perfect! I've found all 7 critical bugs that break your system. Here's the comprehensive analysis and fixes:


🚨 CRITICAL BUGS FOUND

Bug #1: Missing ALL NEXT_PUBLIC_ Environment Variables ⚠️ BLOCKING

Problem: Your .env file has ZERO NEXT_PUBLIC_ variables. The code can't access any configuration.

Evidence:

cat .env | grep "NEXT_PUBLIC"
# Result: No NEXT_PUBLIC variables found!

Required but missing:

  • NEXT_PUBLIC_SUPABASE_URL
  • NEXT_PUBLIC_SUPABASE_ANON_KEY
  • NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
  • NEXT_PUBLIC_STRIPE_STANDARD_PRICE_ID

Bug #2: Provider Doesn't Wrap Children ⚠️ BLOCKING

Problem: The provider is just a wrapper that returns the popup, but doesn't accept or render children.

Current code:

// components/providers/purchase-credits-provider.tsx
export function PurchaseCreditsProvider() {
  return <PurchaseCreditsPopup />; // ❌ No children prop
}

Layout usage:

<PurchaseCreditsProvider /> {/* ❌ Self-closing, app never renders */}

Impact: Nothing renders after the provider because it doesn't pass through children.


Bug #3: useCurrentUser Infinite Loop ⚠️ BLOCKING

Problem: Hook depends on supabase.auth object reference which changes every render.

Code:

useEffect(() => {
  // ...
}, [supabase.auth]); // ❌ Object reference changes every render

Impact: Causes infinite re-renders, crashes browser tab after a few seconds.


Bug #4: CreditsDisplay Uses Wrong Hook ⚠️ HIGH

Problem: Uses useAuth from v0 instead of useCurrentUser.

import { useAuth } from '@/lib/auth/v0/use-auth'; // ❌ Wrong hook

Impact: May not work with current auth setup.


Bug #5: Environment Variable Name Mismatch ⚠️ HIGH

Code expects:

process.env.NEXT_PUBLIC_STRIPE_STANDARD_PRICE_ID

.env has:

STANDARD_TIER_PRICE_ID=price_...  # Missing NEXT_PUBLIC_ prefix

Bug #6: Layout Provider Integration Wrong ⚠️ MEDIUM

Current:

<Sidebar />
<PurchaseCreditsProvider /> {/* ❌ Sibling, not wrapper */}
<Footer />

Should be wrapping the authenticated section.


Bug #7: Popup Auto-Show Logic Flawed ⚠️ LOW

Problem: Uses sessionStorage but has complex dependency issues with the infinite loop.


✅ COMPREHENSIVE FIX PLAN

Complete Fix: Stripe Payment Popup & Runtime Stability

Overview: Fix all 7 critical bugs to make popup show and prevent crashes.

Changes:

  1. Add missing NEXT_PUBLIC environment variables
  2. Fix useCurrentUser infinite loop
  3. Rewrite provider to properly wrap children
  4. Fix CreditsDisplay to use correct hook
  5. Update layout integration
  6. Fix environment variable references
  7. Update .env.example with all required vars

📋 TESTING CHECKLIST

After applying all fixes:

✅ Environment Setup

  • Add all NEXT_PUBLIC_ variables to .env
  • Add all variables to Vercel environment variables
  • Redeploy on Vercel

✅ Local Testing

  • Run npm run dev
  • Sign in with OAuth
  • Popup should appear after 2 seconds ✅
  • Credits display should show in History sidebar ✅
  • Click "Upgrade to Standard" → Stripe checkout opens ✅
  • No console errors ✅
  • No infinite re-renders ✅

✅ Production Testing

  • Deploy to Vercel
  • Complete payment flow
  • Verify webhook updates credits
  • Check Vercel logs for any errors

🎯 ROOT CAUSE SUMMARY

Bug Symptom Fix
Missing NEXT_PUBLIC vars Nothing works Add to .env + Vercel
Provider doesn't wrap children App doesn't render Add children prop
useCurrentUser infinite loop Browser tab crashes Remove object dependency
Wrong hook in CreditsDisplay May not work Use useCurrentUser
Env var name mismatch Stripe checkout fails Use NEXT_PUBLIC_ prefix
Provider as sibling Popup never shows Wrap children properly

🚀 EXPECTED FLOW AFTER FIXES

  1. User signs in → useCurrentUser loads without infinite loop
  2. 2 seconds later → Popup appears
  3. User clicks History → Credits display shows
  4. User clicks "Upgrade" → Stripe checkout opens
  5. User completes payment → Webhook updates credits to 8000
  6. User refreshes → Credits display shows 8000

Would you also like me to add a manual "Upgrade" button in the header for users who dismiss the popup?


💡 Use @coderabbitai plan to create a plan for code changes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants