Skip to content

Commit 6bb3e8f

Browse files
committed
credits deduction implementation, real time updating w optimistic updates and db validation/refund
1 parent c9eeb3e commit 6bb3e8f

File tree

11 files changed

+666
-132
lines changed

11 files changed

+666
-132
lines changed

CREDIT_SYSTEM.md

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
# Credit Deduction System
2+
3+
This document explains how the credit deduction system is implemented in the fullstack chat application.
4+
5+
## Overview
6+
7+
The credit system deducts 1 credit from the user's account for every LLM request made through the chat interface. This includes:
8+
9+
- Sending new messages
10+
- Regenerating AI responses
11+
- Editing messages (which triggers a new LLM request)
12+
13+
## Implementation
14+
15+
### Core Components
16+
17+
1. **`deductUserCredits` function** (`src/lib/stripe.ts`)
18+
19+
- Handles the actual credit deduction logic
20+
- Updates user's credit balance in Supabase
21+
- Returns success status and new balance
22+
23+
2. **`useCreditDeduction` hook** (`src/hooks/use-credit-deduction.ts`)
24+
25+
- Centralized hook for credit deduction logic
26+
- Handles authentication checks
27+
- Provides user-friendly error messages
28+
- Shows toast notifications
29+
- **NEW**: Integrates with global credits context for real-time UI updates
30+
31+
3. **`CreditsProvider` context** (`src/providers/Credits.tsx`)
32+
33+
- **NEW**: Global state management for credits
34+
- Provides optimistic updates for instant UI feedback
35+
- Handles credit fetching and error states
36+
- Enables real-time credit balance updates across the app
37+
38+
4. **`CreditBalance` component** (`src/components/credits/credit-balance.tsx`)
39+
40+
- **NEW**: Real-time credit balance display with Badge design
41+
- Color-coded indicators (red/orange/green) based on credit levels
42+
- Automatic updates when credits are deducted
43+
- Loading and error states
44+
45+
5. **Credit integration points:**
46+
- `src/components/thread/index.tsx` - Main message submission and regeneration
47+
- `src/components/thread/messages/human.tsx` - Message editing
48+
49+
### How It Works
50+
51+
1. **Pre-request validation**: Before any LLM request is made, the system:
52+
53+
- Checks if the user is authenticated
54+
- **NEW**: Optimistically deducts credits from UI immediately
55+
- Attempts to deduct credits from database
56+
- Only proceeds with the LLM request if credits are successfully deducted
57+
- **NEW**: Reverts optimistic update if deduction fails
58+
59+
2. **Real-time UI Updates**:
60+
61+
- Credits are deducted from the UI instantly for immediate feedback
62+
- Database is updated in the background
63+
- UI is refreshed with actual balance from server
64+
- Failed requests automatically refund credits
65+
66+
3. **Error handling**: If credit deduction fails:
67+
68+
- **NEW**: Automatically reverts optimistic UI updates
69+
- Shows appropriate error messages (insufficient credits, authentication required)
70+
- Prevents the LLM request from being made
71+
- Suggests purchasing more credits when needed
72+
73+
4. **Success feedback**: When credits are deducted:
74+
- **NEW**: UI updates immediately (optimistic)
75+
- Shows a success toast with remaining balance
76+
- Proceeds with the LLM request
77+
- **NEW**: Refreshes with actual balance from server
78+
79+
### User Experience
80+
81+
- **Immediate feedback**: Users see credit deduction instantly in the UI
82+
- **Fail-fast approach**: Prevents unnecessary LLM calls when credits are insufficient
83+
- **Clear messaging**: Descriptive error messages explain what went wrong
84+
- **Real-time balance visibility**: Credit balance updates across all components instantly
85+
- **Automatic error recovery**: Failed requests automatically refund credits
86+
87+
## Credit Balance Display
88+
89+
The `CreditBalance` component (`src/components/credits/credit-balance.tsx`) shows users their current credit balance with:
90+
91+
- **Badge design** with professional styling
92+
- **Real-time updates** when credits are deducted/added
93+
- **Color-coded indicators**:
94+
- **Red**: 0 credits (urgent)
95+
- **Orange**: 1-5 credits (low)
96+
- **Green**: 6+ credits (good)
97+
- **Loading states** with pulse animation
98+
- **Error handling** with fallback displays
99+
- **Number formatting** (1,000 instead of 1000)
100+
101+
## Global State Management
102+
103+
### CreditsProvider Context
104+
105+
The `CreditsProvider` wraps the entire application and provides:
106+
107+
```typescript
108+
interface CreditsContextProps {
109+
credits: number | null; // Current credit balance
110+
loading: boolean; // Loading state
111+
error: string | null; // Error state
112+
refreshCredits: () => Promise<void>; // Refresh from server
113+
updateCredits: (newCredits: number) => void; // Set exact amount
114+
deductCredits: (amount: number) => void; // Optimistic deduction
115+
addCredits: (amount: number) => void; // Optimistic addition
116+
}
117+
```
118+
119+
### Integration Pattern
120+
121+
Components can access and update credits using the context:
122+
123+
```typescript
124+
const { credits, deductCredits, addCredits } = useCreditsContext();
125+
126+
// Optimistic deduction
127+
deductCredits(1);
128+
// Later: refresh with actual balance
129+
refreshCredits();
130+
```
131+
132+
## Database Schema
133+
134+
The credit system relies on the `users` table having these columns:
135+
136+
- `credits_available` (integer): Current credit balance
137+
- `subscription_status` (text): User's subscription status
138+
139+
## Error Scenarios
140+
141+
1. **Insufficient Credits**: User has 0 credits remaining
142+
2. **Authentication Required**: User is not signed in
143+
3. **Database Errors**: Network issues or database connectivity problems
144+
4. **Server Overload**: LangGraph server temporarily unavailable (credits auto-refunded)
145+
146+
## Usage Examples
147+
148+
### Basic credit deduction:
149+
150+
```typescript
151+
const { deductCredits } = useCreditDeduction();
152+
const result = await deductCredits({ reason: "send message" });
153+
if (!result.success) {
154+
return; // Error already handled by hook
155+
}
156+
// Proceed with LLM request
157+
```
158+
159+
### Custom credit amount:
160+
161+
```typescript
162+
const result = await deductCredits({
163+
reason: "premium feature",
164+
creditsToDeduct: 5,
165+
showSuccessToast: false,
166+
});
167+
```
168+
169+
### Using global credit state:
170+
171+
```typescript
172+
const { credits, loading, error } = useCreditsContext();
173+
174+
if (loading) return <Spinner />;
175+
if (error) return <ErrorMessage />;
176+
return <div>Credits: {credits}</div>;
177+
```
178+
179+
## Integration Points
180+
181+
- **Message submission**: `handleSubmit` in Thread component
182+
- **Message regeneration**: `handleRegenerate` in Thread component
183+
- **Message editing**: `handleSubmitEdit` in HumanMessage component
184+
- **Global credit display**: `CreditBalance` component in navbar
185+
- **State management**: `CreditsProvider` in root layout
186+
187+
## Future Enhancements
188+
189+
1. **Variable credit costs**: Different LLM models could cost different amounts
190+
2. **Credit packages**: Bulk credit purchases with discounts
191+
3. **Usage analytics**: Track credit usage patterns
192+
4. **Credit expiration**: Time-based credit expiration
193+
5. **Subscription integration**: Automatic credit replenishment for subscribers
194+
6. **Credit notifications**: Push notifications for low credit warnings
195+
7. **Credit history**: Transaction log for credit usage tracking

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,13 @@ First, clone the repository, or run the [`npx` command](https://www.npmjs.com/pa
1515
```bash
1616
npx create-agent-chat-app
1717
```
18+
1819
Stripe Local webhook testing (stripe-cli)
20+
1921
```
2022
stripe listen --events customer.subscription.created,customer.subscription.updated,customer.subscription.deleted --forward-to localhost:3000/api/webhooks/stripe
21-
```
23+
```
24+
2225
or
2326

2427
```bash

src/app/layout.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { Inter } from "next/font/google";
44
import React from "react";
55
import { NuqsAdapter } from "nuqs/adapters/next/app";
66
import { AuthProvider } from "@/providers/Auth";
7+
import { CreditsProvider } from "@/providers/Credits";
78
import AuthLayout from "./auth-layout";
89

910
const inter = Inter({
@@ -27,7 +28,9 @@ export default function RootLayout({
2728
<body className={inter.className}>
2829
<NuqsAdapter>
2930
<AuthProvider>
30-
<AuthLayout>{children}</AuthLayout>
31+
<CreditsProvider>
32+
<AuthLayout>{children}</AuthLayout>
33+
</CreditsProvider>
3134
</AuthProvider>
3235
</NuqsAdapter>
3336
</body>

src/components/credits-display.tsx

Lines changed: 0 additions & 101 deletions
This file was deleted.

0 commit comments

Comments
 (0)