|
| 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 |
0 commit comments