Skip to content

Conversation

@Vanshika814
Copy link
Contributor

@Vanshika814 Vanshika814 commented Oct 15, 2025

Title

Razorpay webhooks, Pro access control, dynamic CTAs, monthly limits, and stability fixes

Summary

This PR implements secure payments, subscription gating, and UX improvements, and hardens flashcard generation reliability.

Key changes

  • Payments and subscriptions

    • Added Razorpay webhook verification and database updates
    • Centralized subscription logic in subscription.service with canAccessPro and status helpers
    • Server-side protection on /generate-pro with redirect for non-Pro users
    • Removed Stripe endpoints; deprecated /api/checkout-sessions (returns 410)
  • Access control and routing

    • Moved Pro checks server-side in page: /generate-pro
    • Dynamic “Get Started” and “+” buttons routing:
      • Not signed-in → /sign-up
      • Free → /generate
      • Pro → /generate-pro
  • Gemini generation stability

    • Per-request cap to 10 (always), monthly plan caps enforced via DB count:
      • free: 10, basic: 500, pro: 2000, orgs: 10000
    • Model fallback sequence: gemini-1.5-flash → gemini-1.5-flash-latest → gemini-1.5-pro → gemini-pro
    • Robust JSON parsing with extraction and cleanup
    • Clear 404 guidance (API enablement / key validation)
  • UI/UX

    • Simplified /generate-pro UI aligned with /generate
    • Added Pro badge in navbar for Pro users

Testing

  • Pro user can access /generate-pro; free user is redirected to /pricing with upgrade notice.
  • Payment success updates user subscription in DB; access granted immediately.
  • “Get Started” and “+” button redirect correctly by plan and sign-in state.
  • Generating flashcards yields up to 10 per request; returns 403 when monthly cap reached.
  • Suspense warning on /pricing resolved; build succeeds.

Migration/Env

  • Ensure the following env vars are set:
    • RAZORPAY_KEY_ID, RAZORPAY_KEY_SECRET,
    • RAZORPAY_WEBHOOK_SECRET : Accounts and settings -> webhooks -> Add new webhook -> paste the webhook route url -> copy the secret key
    • GEMINI_API_KEY

Fixes #96

Summary by CodeRabbit

  • New Features

    • Pro Flashcard Generator with save/view flow and PRO badge visuals.
    • Subscription status API and client checks enabling entitlement-aware CTAs and navigation.
    • Razorpay payment support and payment handling.
  • Changes

    • Pricing: upgrade prompts, redirects, toasts, and post-payment redirect handling.
    • Generation: plan-based monthly limits and per-request caps.
    • Result flow simplified to URL-based payment status; Stripe removed.
  • Chores

    • Editor settings added; compose/docker config adjusted.

@vercel
Copy link
Contributor

vercel bot commented Oct 15, 2025

@Vanshika814 is attempting to deploy a commit to the devsuraj Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link

coderabbitai bot commented Oct 15, 2025

Walkthrough

Adds Razorpay-first subscription plumbing (webhook, service, hook, API), enforces pro-gated access and plan-based generation limits, replaces Stripe checkout/session logic with Razorpay flows, updates UI routing/CTAs based on entitlement, and includes minor editor and docker-compose tweaks.

Changes

Cohort / File(s) Summary
Editor config
/.vscode/settings.json
Adds terminal.integrated.sendKeybindingsToShell: true.
Docker compose
/docker-compose.yml
Removes top-level version: '3.8'.
Auth page redirect
/src/app/(auth)/sign-in/[[...sign-in]]/page.tsx
Adds redirectUrl="/" prop to both SignIn usages.
Pro generator page
/src/app/(dashboard)/generate-pro/page.tsx
Replaces page with async GenerateProPage: checks Clerk auth, uses subscriptionService.canAccessProFeatures, redirects unauthenticated users to sign-in and non-pro users to pricing, renders FlashcardPro for pro users.
Checkout sessions (Stripe removal)
/src/app/api/checkout-sessions/route.ts
Replaces GET/POST handlers to return 410; removes Stripe logic.
Generate API (rate limits)
/src/app/api/generate/route.ts
Adds plan-based monthly allowance checks, per-request cap (10), counts monthly usage via Prisma, enforces 403 when limits reached before calling generation service.
Razorpay webhook
/src/app/api/razorpay/webhook/route.ts
New POST endpoint: verifies signature, parses events, handles payment.captured / payment.failed / order.paid, updates payments and user subscription via Prisma.
Subscription status API
/src/app/api/user/subscription-status/route.ts
New GET endpoint: authenticates with Clerk and returns subscription status from subscriptionService.
Subscription service
/src/lib/services/subscription.service.ts
New SubscriptionService and SubscriptionStatus interface: get status, plan, expiry, canAccessPro, update subscription; exports subscriptionService.
Subscription hook
/src/hooks/useSubscription.ts
New client hook fetching /api/user/subscription-status, exposes canAccessPro, status fields, loading, and refreshSubscription.
Middleware matcher
/src/middleware.ts
Removes public matcher for /generate-pro(.*); adds /api/razorpay/webhook to public routes; notes Pro validation moved to page.
UI entitlement wiring
/src/app/page.tsx, src/app/flashcards/page.tsx, src/components/Footer.tsx, src/components/Navbar.tsx
Use Clerk + useSubscription to change CTA/link targets and labels; show PRO badge when canAccessPro.
Pricing & result flows
/src/app/pricing/page.tsx, src/app/result/page.tsx
Pricing: split into PricingContent, Suspense, URL param handling, toasts, redirect-after-payment handling. Result: remove Stripe session fetch; use payment query param for success/error display.
Core flashcard components
/src/components/core/flash-card-pro.tsx, src/components/core/flash-card.tsx, src/components/core/generate.tsx
Add FlashcardPro component (local storage drafts, save dialog, save flow); spacing tweak in flash-card; strengthen submission guard/error handling and toasts in generate.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Page as /generate-pro
  participant Clerk
  participant SubSvc as subscriptionService

  User->>Page: GET /generate-pro
  Page->>Clerk: getUser()
  alt not signed in
    Page-->>User: redirect /sign-in
  else signed in
    Page->>SubSvc: canAccessProFeatures(userId)
    alt pro
      Page-->>User: render FlashcardPro
    else not pro
      Page-->>User: redirect /pricing?upgrade=pro
    end
  end
Loading
sequenceDiagram
  autonumber
  actor Client
  participant API as POST /api/generate
  participant SubSvc as subscriptionService
  participant DB as Prisma
  participant Gen as flashcardService

  Client->>API: POST text (text/plain)
  API->>SubSvc: getUserPlan(userId)
  API->>DB: count flashcards this month
  alt limit exceeded
    API-->>Client: 403 monthly limit reached
  else allowed
    API->>Gen: generateFlashcards(text)
    Gen-->>API: generated[]
    API-->>Client: 200 { flashcards }
  end
Loading
sequenceDiagram
  autonumber
  participant Razorpay
  participant Webhook as POST /api/razorpay/webhook
  participant DB as Prisma
  participant SubSvc as subscriptionService

  Razorpay->>Webhook: event + signature
  Webhook->>Webhook: verify signature (HMAC-SHA256)
  alt invalid
    Webhook-->>Razorpay: 400 invalid signature
  else valid
    alt payment.captured
      Webhook->>DB: update payment -> COMPLETED
      Webhook->>SubSvc: updateSubscriptionStatus(userId, plan, ACTIVE, expiresAt)
      Webhook-->>Razorpay: 200
    else payment.failed
      Webhook->>DB: update payment -> FAILED
      Webhook-->>Razorpay: 200
    else order.paid
      Webhook->>DB: mark related payments COMPLETED
      Webhook-->>Razorpay: 200
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Potential focus areas:

  • src/app/api/razorpay/webhook/route.ts (signature verification, timing-safe compare, Prisma updates).
  • src/app/api/generate/route.ts (plan limits, counting logic, edge cases for concurrent requests).
  • src/lib/services/subscription.service.ts and src/hooks/useSubscription.ts (correctness of plan/expiry calculations and error handling).
  • Pages/components wired to entitlement (/generate-pro, Navbar, Footer) to ensure consistent UX and routing.

Possibly related PRs

Suggested labels

Level 2

Suggested reviewers

  • Suraj-kumar00

Poem

"I hopped through commits, small and spry,
swapped Stripe for Razorpay with a twitchy eye.
PRO badge gleams, webhooks drum the tune,
flashcards bloom beneath the moon.
A rabbit cheers — code hopping soon!" 🐰

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Refactor authentication and subscription handling; Update routes and components to support Pro access checks" accurately captures the main thrust of the changeset. The pull request implements comprehensive authentication and subscription refactoring across multiple files, introduces server-side Pro access checks for the /generate-pro route, centralizes subscription logic into a service layer, and updates numerous UI components to respect Pro access status. The title is specific and clear without being overly verbose, effectively communicating the primary changes to a developer scanning the history.
Linked Issues Check ✅ Passed The PR successfully addresses all major coding requirements from issue #96: server-side Pro access enforcement on /generate-pro with Clerk authentication and subscription checks [generate-pro/page.tsx]; plan-based rate limiting with monthly caps and per-request limits [src/app/api/generate/route.ts]; Pro access indicators in the UI via a badge in the navbar [Navbar.tsx]; and an improved Pro generation component with proper UI structure [flash-card-pro.tsx]. Additionally, the PR implements the necessary infrastructure through a new subscription service [subscription.service.ts], Razorpay webhook integration [razorpay/webhook/route.ts], and subscription status endpoint [user/subscription-status/route.ts], along with dynamic routing adjustments across multiple pages and components to direct users based on their sign-in and Pro access status.
Out of Scope Changes Check ✅ Passed The overwhelming majority of changes directly align with the linked issue #96 requirements and PR objectives. A small number of peripheral modifications exist: .vscode/settings.json introduces IDE configuration that is not directly related to the feature requirements, and docker-compose.yml removes the version declaration as a minor infrastructure adjustment. However, all critical changes—including the new subscription service, Pro access checks, rate limiting logic, Razorpay webhook integration, and UI updates—directly serve the stated objectives of restricting Pro access, enforcing rate limits, and improving the Pro plan functionality.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 096e5b9 and 70bb302.

📒 Files selected for processing (1)
  • src/app/api/razorpay/webhook/route.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/api/razorpay/webhook/route.ts (1)
src/lib/database.ts (1)
  • prisma (7-9)

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.

Copy link

@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: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/api/generate/route.ts (1)

56-64: Enforce desiredCount when generating flashcards
generateFlashcards currently ignores desiredCount and always returns 10 cards. In route.ts, either:

  • Extend the service signature to generateFlashcards(text: string, count: number), update its prompt to Create ${count} flashcards…, and pass desiredCount;
  • Or post-process results:
    const all = await flashcardService.generateFlashcards(text);
    const flashcards = all.slice(0, desiredCount);
🧹 Nitpick comments (5)
src/app/result/page.tsx (2)

104-104: Minor formatting: Extra leading whitespace.

Remove the extra space before the <p> tag for consistency.

Apply this diff:

-                             <p className="text-muted-foreground mb-4">Your payment failed. Please try again.</p>
+                            <p className="text-muted-foreground mb-4">Your payment failed. Please try again.</p>

15-31: ResultContent should verify payment server-side instead of trusting the URL param. While you verify payment in src/app/pricing/page.tsx via /api/razorpay/verify-payment, the result page still reads payment directly from the query. This can confuse users who craft URLs. In src/app/result/page.tsx, fetch and confirm the user’s subscription or payment status from a server endpoint (or use a signed, time-limited token) before showing success.

src/app/api/razorpay/webhook/route.ts (1)

62-127: Validate payment amount against expected plan pricing.

The webhook processes payments without verifying that the captured amount matches the expected price for the subscription plan. This could allow users to gain Pro access by paying incorrect amounts if the order/payment records are manipulated.

Add a validation step before updating the subscription:

     if (order) {
+      // Validate payment amount matches expected plan price
+      const expectedAmounts = {
+        free: 0,
+        basic: 500,  // Example prices in paise/cents
+        pro: 2000,
+        orgs: 10000
+      };
+      
+      const expectedAmount = expectedAmounts[order.plan as keyof typeof expectedAmounts];
+      if (payment.amount !== expectedAmount) {
+        console.error('❌ Payment amount mismatch:', {
+          expected: expectedAmount,
+          received: payment.amount,
+          plan: order.plan
+        });
+        return;
+      }
+      
       // Calculate subscription dates
src/components/core/flash-card-pro.tsx (1)

18-91: Reduce code duplication with the standard flashcard component.

FlashcardPro duplicates 90+ lines from flash-card.tsx with only minor differences (local storage key, icon, badge, button styling). This violates the DRY principle and increases maintenance burden.

Refactor into a single component with a prop to toggle Pro features:

// flash-card.tsx
interface FlashcardProps {
  isPro?: boolean;
}

export default function Flashcard({ isPro = false }: FlashcardProps) {
  const { user } = useUser();
  const storageKey = isPro ? 'draft-flashcards-pro' : 'draft-flashcards';
  const [flashcards, setFlashcards] = useLocalStorage<FlashcardInput[]>(storageKey, []);
  // ... rest of logic
  
  return (
    <div className="flex flex-col items-center justify-center min-h-screen p-4 md:p-10">
      <Card className="w-full lg:max-w-[70vw]">
        <CardHeader>
          {isPro ? (
            <div className="flex items-center justify-between">
              <CardTitle className="flex items-center space-x-2">
                <Crown className="w-5 h-5 text-purple-600" />
                <span>Pro Flashcard Generator</span>
              </CardTitle>
              <Badge variant="secondary" className="bg-purple-100 text-purple-800">
                PRO
              </Badge>
            </div>
          ) : (
            <CardTitle>Flashcard Generator</CardTitle>
          )}
        </CardHeader>
        {/* ... rest of component */}
      </Card>
    </div>
  );
}

Then use <Flashcard isPro={true} /> in the Pro page.

src/lib/services/subscription.service.ts (1)

99-121: Consider adding type safety for plan and status parameters.

The updateSubscriptionStatus method accepts plan and status as strings without validation. Invalid values could be persisted to the database.

Add type guards or enums:

// types.ts
export enum SubscriptionPlan {
  Free = 'free',
  Basic = 'basic',
  Pro = 'pro',
  Orgs = 'orgs'
}

export enum SubscriptionStatus {
  Active = 'active',
  Expired = 'expired',
  Cancelled = 'cancelled'
}

// subscription.service.ts
async updateSubscriptionStatus(
  userId: string, 
  plan: SubscriptionPlan,  // typed parameter
  status: SubscriptionStatus,  // typed parameter
  expiresAt: Date
): Promise<void> {
  // ... implementation
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6aa043c and dc6716b.

📒 Files selected for processing (20)
  • .vscode/settings.json (1 hunks)
  • docker-compose.yml (0 hunks)
  • src/app/(auth)/sign-in/[[...sign-in]]/page.tsx (1 hunks)
  • src/app/(dashboard)/generate-pro/page.tsx (1 hunks)
  • src/app/api/checkout-sessions/route.ts (1 hunks)
  • src/app/api/generate/route.ts (2 hunks)
  • src/app/api/razorpay/webhook/route.ts (1 hunks)
  • src/app/api/user/subscription-status/route.ts (1 hunks)
  • src/app/flashcards/page.tsx (2 hunks)
  • src/app/page.tsx (3 hunks)
  • src/app/pricing/page.tsx (4 hunks)
  • src/app/result/page.tsx (3 hunks)
  • src/components/Footer.tsx (2 hunks)
  • src/components/Navbar.tsx (4 hunks)
  • src/components/core/flash-card-pro.tsx (1 hunks)
  • src/components/core/flash-card.tsx (1 hunks)
  • src/components/core/generate.tsx (2 hunks)
  • src/hooks/useSubscription.ts (1 hunks)
  • src/lib/services/subscription.service.ts (1 hunks)
  • src/middleware.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • docker-compose.yml
🧰 Additional context used
🧬 Code graph analysis (14)
src/app/api/checkout-sessions/route.ts (1)
src/app/api/razorpay/webhook/route.ts (1)
  • POST (8-60)
src/lib/services/subscription.service.ts (1)
src/lib/database.ts (1)
  • prisma (7-9)
src/components/Navbar.tsx (1)
src/hooks/useSubscription.ts (1)
  • useSubscription (15-62)
src/hooks/useSubscription.ts (1)
src/lib/services/subscription.service.ts (1)
  • SubscriptionStatus (3-9)
src/app/page.tsx (1)
src/hooks/useSubscription.ts (1)
  • useSubscription (15-62)
src/app/flashcards/page.tsx (1)
src/hooks/useSubscription.ts (1)
  • useSubscription (15-62)
src/components/Footer.tsx (1)
src/hooks/useSubscription.ts (1)
  • useSubscription (15-62)
src/app/api/razorpay/webhook/route.ts (1)
src/lib/database.ts (1)
  • prisma (7-9)
src/app/(dashboard)/generate-pro/page.tsx (2)
src/lib/services/subscription.service.ts (1)
  • subscriptionService (124-124)
src/components/core/flash-card-pro.tsx (1)
  • FlashcardPro (18-91)
src/components/core/flash-card.tsx (1)
src/components/ui/button.tsx (1)
  • Button (56-56)
src/components/core/flash-card-pro.tsx (7)
src/lib/hooks/useLocalStorage.ts (1)
  • useLocalStorage (3-31)
src/types/index.ts (1)
  • FlashcardInput (52-55)
src/components/common/ErrorBoundary.tsx (1)
  • ErrorBoundary (19-82)
src/components/flashcards/FlashcardGenerator.tsx (1)
  • FlashcardGenerator (33-107)
src/components/common/LoadingSpinner.tsx (1)
  • LoadingSpinner (10-31)
src/components/flashcards/FlashcardViewer.tsx (1)
  • FlashcardViewer (16-123)
src/components/flashcards/FlashcardSaveDialog.tsx (1)
  • FlashcardSaveDialog (24-186)
src/app/pricing/page.tsx (1)
src/components/ui/use-toast.ts (2)
  • useToast (194-194)
  • toast (194-194)
src/app/api/generate/route.ts (3)
src/lib/services/subscription.service.ts (1)
  • subscriptionService (124-124)
src/lib/database.ts (1)
  • prisma (7-9)
src/types/index.ts (1)
  • ApiError (124-130)
src/app/api/user/subscription-status/route.ts (1)
src/lib/services/subscription.service.ts (1)
  • subscriptionService (124-124)
🪛 Biome (2.1.2)
src/components/core/generate.tsx

[error] 68-68: Catch clause variable type annotation must be 'any' or 'unknown' if specified.

(parse)

🔇 Additional comments (38)
src/app/result/page.tsx (3)

1-13: LGTM!

The imports are appropriate and the comment clearly indicates the architectural shift away from Stripe.


33-122: LGTM!

The UI structure is well-organized with clear success, error, and failure states. The responsive design with mobile-first approach, loading skeleton, and actionable CTAs provide a good user experience.


124-163: LGTM!

Excellent use of React Suspense with a matching skeleton fallback. This follows Next.js best practices for handling search params and provides a smooth loading experience.

src/components/core/flash-card.tsx (1)

72-72: LGTM! Consistent spacing improvement.

The added top margin improves vertical spacing for the "View Saved Flashcards" button and aligns with the same spacing applied in the Pro variant.

src/lib/services/subscription.service.ts (1)

15-70: LGTM! Robust subscription status handling.

The method correctly:

  • Handles missing users with safe defaults
  • Computes expiration status
  • Restricts Pro access to paid plans (basic, pro, orgs)
  • Logs detailed status for debugging
  • Returns safe defaults on errors
src/app/flashcards/page.tsx (1)

11-11: LGTM! Pro-aware navigation logic.

The integration of useSubscription correctly routes users to /generate-pro when they have Pro access, and /generate otherwise. This aligns with the broader Pro-access flow introduced in the PR.

Also applies to: 18-18, 71-71

src/app/(dashboard)/generate-pro/page.tsx (1)

6-24: LGTM! Server-side Pro access enforcement.

The async page component correctly:

  • Validates authentication and redirects to sign-in with a return URL
  • Checks Pro access via subscriptionService before rendering
  • Redirects to pricing with clear upgrade prompt if access is denied
  • Renders FlashcardPro only for authorized Pro users

This server-side approach provides robust access control and aligns with the PR's goal of moving Pro checks out of middleware.

src/app/api/checkout-sessions/route.ts (1)

1-14: Approve removal of Stripe checkout endpoints
No occurrences of /api/checkout-sessions or checkout-sessions found in the codebase; safe to finalize.

src/app/(auth)/sign-in/[[...sign-in]]/page.tsx (1)

12-12: LGTM! Clean post-auth redirect.

The redirectUrl="/" prop ensures users land at the root after sign-in, aligning with the subscription-aware routing introduced in the broader PR.

Also applies to: 16-16

src/app/page.tsx (3)

12-13: Good integration of authentication and subscription hooks.

The imports and hook usage correctly integrate Clerk's authentication with the custom subscription hook to enable entitlement-based routing.

Also applies to: 23-24


110-110: Correct CTA routing logic.

The conditional routing properly directs guests to sign-up, Pro users to /generate-pro, and non-Pro authenticated users to /generate, aligning with the subscription-aware navigation introduced in this PR.


114-114: Clear and contextual CTA labels.

The button labels appropriately reflect the user's authentication and subscription state, providing clear direction to users.

src/app/api/user/subscription-status/route.ts (3)

7-14: Proper authentication guard.

The authentication check correctly uses Clerk v6's await auth() pattern and returns a clear 401 response for unauthorized requests.


16-18: Clean service delegation.

The route properly delegates subscription logic to the service layer and returns a clean JSON response.


20-26: Appropriate error handling.

The error handling logs errors for debugging and returns a generic 500 response, avoiding information leakage.

src/components/Navbar.tsx (4)

13-15: Correct imports for Pro badge feature.

The new imports support the subscription-aware UI enhancements, including the Pro badge display.


22-22: Clean hook integration.

The subscription hook is properly integrated to access Pro status and loading state.


153-159: Well-guarded Pro badge display.

The loading check prevents badge flashing, and the canAccessPro guard ensures accurate Pro status display.


126-126: Consistent subscription-aware navigation.

Desktop and mobile CTAs correctly route based on authentication and subscription state, maintaining consistency across the navbar.

Also applies to: 130-130, 260-260, 264-264

src/hooks/useSubscription.ts (3)

15-24: Safe default state initialization.

The hook initializes with secure defaults (free plan, no Pro access) and loading: true to handle the initial fetch state.


26-33: Proper effect dependencies and guards.

The useEffect correctly waits for Clerk's isLoaded state and handles the unauthenticated case by setting loading to false.


35-61: Robust fetch and refresh logic.

The fetch function includes proper error handling and response validation. The refreshSubscription helper correctly manages loading state during re-fetches.

src/app/api/generate/route.ts (4)

10-15: Correct authentication pattern.

The authentication guard properly uses Clerk v6's async auth() and returns a clear 401 for unauthorized requests.


17-22: Thorough input validation.

The input validation correctly checks for missing or empty text content, including whitespace-only inputs.


24-34: Well-defined plan-based rate limits.

The tiered monthly caps are reasonable and properly fetched from the subscription service. The fallback to the free tier (10) provides a safe default.


36-54: Robust monthly usage tracking and limit enforcement.

The UTC-based month calculation ensures consistency across timezones, and the usage query correctly counts flashcards created this month. The 403 response with descriptive error message provides clear feedback when limits are reached.

src/app/pricing/page.tsx (4)

15-16: Good Suspense integration for useSearchParams.

The component split into PricingContent and Page wrapper with Suspense correctly resolves the Next.js warning about useSearchParams usage. This aligns with the PR objectives stating "suspense warning on /pricing resolved."

Also applies to: 42-42, 600-606


45-45: Clear upgrade flow feedback.

The upgrade detection and toast notification provide clear user feedback when users are redirected to pricing due to Pro access requirements, improving the UX of the subscription flow.

Also applies to: 53-64


170-175: Flexible post-payment redirect.

The conditional redirect allows users to return to their original page after upgrading (when redirectUrl is provided) or defaults to the result page, providing a smooth payment flow.


50-50: Comprehensive toast-based feedback.

The toast notifications throughout the payment flow provide clear, user-friendly feedback for all payment states (setup, success, verification, errors, cancellation), improving the UX over traditional alerts.

Also applies to: 58-64, 98-101, 137-140, 162-166, 182-186, 196-200, 219-223, 235-239

src/middleware.ts (2)

10-11: Correct public route configuration.

Adding /api/razorpay/webhook to public routes enables external Razorpay callbacks, and removing /generate-pro from public routes aligns with the Pro access gating strategy.


18-18: Clear architectural documentation.

The comments clearly document that Pro access validation is now handled at the page component level due to Prisma Edge Runtime constraints, providing helpful context for future maintainers.

Also applies to: 26-27

src/components/core/generate.tsx (6)

34-34: LGTM! Submission state tracking added.

The isSubmitting state effectively prevents race conditions and provides the foundation for disabling the UI during generation.


40-41: LGTM! Concurrent submission guard in place.

The early return when already submitting is a good defensive measure. Since the button is disabled during submission (line 101), users receive visual feedback, making the silent early return acceptable.


45-45: LGTM! Content-Type header matches body format.

Setting Content-Type: text/plain is correct when sending the text string directly in the request body.


49-56: LGTM! Improved error handling with graceful fallback.

The error handling properly attempts to parse API error details (lines 51-54) and falls back to a default message if parsing fails. The empty catch block is acceptable here since a fallback message is already in place.


101-103: LGTM! Proper button state management.

The button correctly disables during submission and displays a loading message, providing clear feedback to users.


58-63: Destructuring matches API response shape: the handler returns { flashcards: Flashcard[] }, so const { flashcards: generated } = await response.json() is correct.

Copy link

@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

♻️ Duplicate comments (2)
src/components/Footer.tsx (1)

102-103: Routing logic now aligns with PR objectives.

The conditional routing correctly implements the stated behavior:

  • Non-signed-in users → /sign-up
  • Signed-in Free users → /generate
  • Signed-in Pro users → /generate-pro

This resolves the routing inconsistency flagged in the previous review.

src/components/core/generate.tsx (1)

68-68: Fix invalid catch clause type annotation.

TypeScript and Biome require catch clause variables to be typed as any, unknown, or have the type annotation omitted. The union type Error | unknown is invalid because unknown is already the top type.

Apply this diff to fix the type annotation:

-        } catch (error: Error | unknown) {
+        } catch (error: unknown) {
             console.error('Error generating flashcards:', error)
             toast({
                 title: "Error",
                 description: error instanceof Error ? error.message : 'An unexpected error occurred',
                 variant: "destructive",
             });

Note: The runtime handling at line 72 is already correct with the instanceof Error check.

🧹 Nitpick comments (1)
src/app/api/razorpay/webhook/route.ts (1)

43-62: Validate event payload shape (schema) before dispatch.

Guard against undefined payload structure and reduce runtime errors. Use a minimal zod schema or narrow by in-operator checks before accessing nested fields.

I can provide a small zod schema for payment/order events if helpful.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc6716b and 096e5b9.

📒 Files selected for processing (4)
  • src/app/api/razorpay/webhook/route.ts (1 hunks)
  • src/components/Footer.tsx (2 hunks)
  • src/components/core/generate.tsx (2 hunks)
  • src/hooks/useSubscription.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/hooks/useSubscription.ts
🧰 Additional context used
🧬 Code graph analysis (2)
src/app/api/razorpay/webhook/route.ts (1)
src/lib/database.ts (1)
  • prisma (7-9)
src/components/Footer.tsx (1)
src/hooks/useSubscription.ts (1)
  • useSubscription (15-63)
🪛 Biome (2.1.2)
src/components/core/generate.tsx

[error] 68-68: Catch clause variable type annotation must be 'any' or 'unknown' if specified.

(parse)

🔇 Additional comments (8)
src/components/Footer.tsx (1)

7-13: LGTM! Auth and subscription state integration is correct.

The addition of useUser and useSubscription hooks properly provides the authentication and entitlement state needed for conditional routing. The canAccessPro defaults to false during loading, which safely falls back to non-pro paths until the subscription status is fetched.

src/components/core/generate.tsx (3)

34-34: LGTM! Submission state management prevents double submissions.

The isSubmitting state with guard logic and finally block correctly prevents concurrent submissions while ensuring the button state is always reset. The disabled button with "Generating..." label provides clear user feedback.

Also applies to: 40-41, 75-77, 101-102


45-45: LGTM! Content-Type aligns with plain text body.

Setting Content-Type: text/plain is correct since data.text is sent directly as the request body without JSON serialization.


50-56: LGTM! Enhanced error handling and response validation.

The improvements provide better user feedback:

  • Attempts to parse error details from JSON responses with graceful fallback
  • Validates flashcards data structure before updating state
  • Shows flashcard count in success toast

Also applies to: 58-66

src/app/api/razorpay/webhook/route.ts (4)

12-21: Good: runtime flags and env check are correct for Next App Router.

force-dynamic + nodejs is appropriate here, and checking the secret inside the handler avoids build-time failures. LGTM.


56-59: Clarify order.paid responsibilities vs payment.captured.

Currently order.paid only flips payment statuses, while activation happens on payment.captured. Confirm Razorpay’s delivery order guarantees so users aren’t left unactivated if only order.paid arrives.

If needed, enrich order.paid to perform activation when a matching, not-yet-activated payment exists (idempotently).

Also applies to: 166-184


22-28: Optional: confirm Razorpay retry policy for 4xx.

If Razorpay retries on non-2xx, 400 for invalid/missing signature may cause repeated calls. Confirm expected behavior and adjust (e.g., 200 with logged rejection) if necessary to avoid webhook spam.


114-121: [running verification]

#!/bin/bash
# Locate payment assignment and Razorpay usage in route.ts
rg -nP "const\s+payment\s*=" -C3 src/app/api/razorpay/webhook/route.ts
rg -n "payment.id" -C3 src/app/api/razorpay/webhook/route.ts
rg -n "new Razorpay" -C3 src/app/api/razorpay/webhook/route.ts
rg -n "import .*razorpay" -C3 src/app/api/razorpay/webhook/route.ts

@vercel
Copy link
Contributor

vercel bot commented Oct 15, 2025

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

Project Deployment Preview Comments Updated (UTC)
flash-fathom-ai Ready Ready Preview Comment Nov 1, 2025 4:27pm

@Suraj-kumar00 Suraj-kumar00 self-requested a review October 15, 2025 17:21
@Suraj-kumar00 Suraj-kumar00 removed their assignment Oct 15, 2025
@Suraj-kumar00 Suraj-kumar00 added bug Something isn't working enhancement New feature or request hacktoberfest Hacktoberfest 2025 event project gssoc2025 GSSoC 2025 labels Oct 15, 2025
@Vanshika814
Copy link
Contributor Author

@Suraj-kumar00 have you reviewed my PR ?

@Suraj-kumar00
Copy link
Owner

Hi @Vanshika814, sorry for the delay.

Could you please add the section so that user can find to navigate to the pro subscription.

You how the other platform give option to upgrade and then redirect to the pricing and when the payment is done in the user's profile it show whatever plan they have purchased right!

So one more thing I want to ask that how the payment subscription is being handled, like will fake and real payment work or only the real payment will work and if so what I have to do from my end like adding the secrets or what ever steps are there please let me know.

Let me know if you can close this tomorrow so that I can merge this PR ASAP.

Thanks for your contribution @Vanshika814 ...

@Suraj-kumar00
Copy link
Owner

Also do follow the suggestion of the coderabbitai.

@Vanshika814
Copy link
Contributor Author

Hi @Vanshika814, sorry for the delay.

Could you please add the section so that user can find to navigate to the pro subscription.

You how the other platform give option to upgrade and then redirect to the pricing and when the payment is done in the user's profile it show whatever plan they have purchased right!

So one more thing I want to ask that how the payment subscription is being handled, like will fake and real payment work or only the real payment will work and if so what I have to do from my end like adding the secrets or what ever steps are there please let me know.

Let me know if you can close this tomorrow so that I can merge this PR ASAP.

Thanks for your contribution @Vanshika814 ...

yeah sure i'll try to make all the changes by today.

@Vanshika814
Copy link
Contributor Author

Could you please add the section so that user can find to navigate to the pro subscription.

You how the other platform give option to upgrade and then redirect to the pricing and when the payment is done in the user's profile it show whatever plan they have purchased right!

for this what we can do is the pricing page is already there, so for the users we can change it's name to upgrade to pro or something like this. and then redirect the user to pricing page.

@Vanshika814
Copy link
Contributor Author

So one more thing I want to ask that how the payment subscription is being handled, like will fake and real payment work or only the real payment will work and if so what I have to do from my end like adding the secrets or what ever steps are there please let me know.

real payments are being handled

Steps to setup:

  • open the razorpay dashboard in live mode

  • navigate to the account and settings

  • navigate to the webhooks option

  • click the 'Add new Webhook'

  • config will be :

    1. Webhook Url - https://your-domain/api/razorpay/webhook
    2. Secret - click on the text box the razorpay will provide you with the secret key copy that key and paste it in env file with RAZORPAY_WEBHOOK_SECRET="your razorpay secret"
    3. Active events - select payment.captured, payment.failed, order.paid
    4. final step - click create webhook

…and update payment record lookup to include fallback by order ID
@Suraj-kumar00
Copy link
Owner

Hey @Vanshika814 everything looks good to me... merging the PR.

Thanks again for contributing to FlashFathomAI.

@Suraj-kumar00 Suraj-kumar00 merged commit d3be8df into Suraj-kumar00:main Nov 1, 2025
3 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working enhancement New feature or request gssoc2025 GSSoC 2025 hacktoberfest Hacktoberfest 2025 event project

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Free Users Can Access Pro Plan on /generate-pro Page

2 participants