Credit System
How the credit system works in Chameleon
Credit System
Chameleon uses a credit-based system to manage AI generation usage and reward user activities.
Overview
Credits are the internal currency used for:
- AI Generation - Text, image, and video generation
- User Rewards - Daily check-ins, referrals
- Service Access - Premium features
Credit Sources
New User Bonus
Every new user receives 100 credits upon registration.
// Automatically added on account creation
Credits: 100
Expires: 1 year from signup
Transaction Type: new_user
Purchase Credits
Users can buy credit packages through the pricing page.
Example Packages:
- Starter: 1,000 credits
- Professional: 5,000 credits
- Enterprise: 20,000 credits
Payment Methods:
- One-time purchase (expires in 1 year)
- Monthly subscription (credits reset monthly)
- Yearly subscription (credits reset yearly)
Daily Check-In
Users earn 1 credit per day by visiting /api/ping.
// API endpoint
POST /api/ping
// Response
{
"code": 0,
"message": "Ping success, 1 credit added"
}
Implementation:
- Maximum 1 credit per day per user
- Resets at midnight (UTC)
- Simple way to engage users
Referral Rewards
When a referred user makes a purchase:
- Referrer earns commission (5-30% based on order value)
- Converted to credits automatically
See Referral System for details.
Admin Added Credits
Admins can manually add credits to user accounts for:
- Customer support
- Promotions
- Compensation
// In admin panel or via API
import { increaseCredits } from "@/services/credit";
await increaseCredits({
user_uuid: "user-uuid",
trans_type: CreditsTransType.SystemAdd,
credits: 500,
expired_at: "2026-12-31T23:59:59Z",
});
Credit Costs
AI Generation Costs
Different AI providers and types have different credit costs:
Video Generation
- Kling AI: 10 credits per video
- Seedance: 10 credits per video
Image Generation
- OpenAI DALL-E 3: 5 credits per image
- Replicate Flux: 3 credits per image
- Kling: 4 credits per image
Text Generation (if enabled)
- OpenAI GPT-4: 1 credit per request
- DeepSeek: 1 credit per request
- OpenRouter: 1 credit per request
- SiliconFlow: 1 credit per request
Configuration: See src/services/constant.ts - AI_GENERATION_CREDITS
Custom Costs
You can adjust credit costs in the code:
// src/services/constant.ts
export const AI_GENERATION_CREDITS = {
image: {
openai: 5, // DALL-E 3
replicate: 3, // Flux
kling: 4
},
video: {
kling: 10,
seedance: 10
},
};
Credit Deduction
Automatic Deduction
Credits are automatically deducted when:
- AI generation completes successfully
- User uses premium features
Deduction Timing
For AI Generation:
- Credits are NOT deducted on task creation
- Credits are deducted ONLY on successful completion
- If generation fails, no credits are deducted
// In src/services/generation.ts
try {
// Generate AI content
const result = await generateVideo(...);
// Mark as completed
await updateGenerationStatus(gen_id, "completed", resultUrls);
// Deduct credits (only on success)
await decreaseCredits({
user_uuid,
trans_type: CreditsTransType.AIGeneration,
credits: credits_cost,
});
} catch (error) {
// Mark as failed
await updateGenerationStatus(gen_id, "failed", error.message);
// No credits deducted on failure
throw error;
}
Insufficient Credits
If a user doesn't have enough credits:
const userCredits = await getUserCredits(user_uuid);
if (userCredits.left_credits < credits_cost) {
return respErr("Insufficient credits. Please recharge.");
}
The UI shows:
- Warning message: "Insufficient credits"
- Link to pricing page: "Recharge"
- Current balance display
Credit Expiration
Expiration Rules
| Credit Source | Expiration |
|---|---|
| New User Bonus | 1 year from signup |
| One-Time Purchase | 1 year from purchase |
| Monthly Subscription | End of monthly cycle |
| Yearly Subscription | End of yearly cycle |
| Referral Rewards | 1 year from earning |
| System Added | Custom (set by admin) |
Expiration Logic
// Check if credit is expired
const now = new Date();
const expiredAt = new Date(creditTrans.expired_at);
if (expiredAt < now) {
// Credit has expired, cannot use
continue;
}
FIFO Deduction
Credits are deducted in First-In-First-Out order:
- Oldest credits are used first
- Prevents waste of expiring credits
- Implemented in
decreaseCreditsfunction
Credit Transactions
Transaction Types
All credit changes are recorded in credits_trans table:
export enum CreditsTransType {
NewUser = "new_user", // New user bonus
OrderPay = "order_pay", // Purchased credits
SystemAdd = "system_add", // Admin added
Ping = "ping", // Daily check-in
AIGeneration = "ai_generation", // AI usage
}
Transaction History
Users can view their credit history at /my-credits:
- Transaction type
- Amount (+ or -)
- Remaining balance
- Expiration date
- Timestamp
Query Transactions
import { getCreditsByUserUuid } from "@/models/credit";
const transactions = await getCreditsByUserUuid(user_uuid, page, pageSize);
// Returns array of transactions with:
// - trans_type
// - credits (positive or negative)
// - expired_at
// - created_at
Credit Balance
Checking Balance
API Endpoint:
POST /api/get-user-credits
// Response
{
"code": 0,
"data": {
"left_credits": 850,
"total_credits": 1100
}
}
In Code:
import { getUserCredits } from "@/services/credit";
const credits = await getUserCredits(user_uuid);
console.log(credits.left_credits); // Current balance
console.log(credits.total_credits); // Lifetime earned
Balance Display
The credit balance is displayed:
- Header - Top right of user center
- AI Generator - Shows cost and remaining credits
- My Credits page - Detailed breakdown
Managing Credits
Increase Credits
import { increaseCredits, CreditsTransType } from "@/services/credit";
await increaseCredits({
user_uuid: "user-uuid",
trans_type: CreditsTransType.OrderPay,
credits: 1000,
expired_at: "2026-01-01T00:00:00Z",
});
Decrease Credits
import { decreaseCredits, CreditsTransType } from "@/services/credit";
await decreaseCredits({
user_uuid: "user-uuid",
trans_type: CreditsTransType.AIGeneration,
credits: 10,
});
decreaseCredits automatically handles expired credits and FIFO deduction.
Credit System Architecture
Database Tables
credits Table:
- Stores current balance (
left_credits) - Stores lifetime total (
total_credits) - Updated on every transaction
credits_trans Table:
- Stores all credit transactions
- Immutable audit log
- Used for history and analytics
Service Layer
File: src/services/credit.ts
Key Functions:
getUserCredits(user_uuid)- Get current balanceincreaseCredits(...)- Add creditsdecreaseCredits(...)- Deduct creditsgetCreditsByUserUuid(...)- Get transaction history
Constants
File: src/services/constant.ts
export const CreditsAmount = {
NewUserGet: 100, // New user bonus
OneDayPingGet: 1, // Daily check-in
};
export const AI_GENERATION_CREDITS = {
image: { openai: 5, replicate: 3, kling: 4 },
video: { kling: 10, seedance: 10 },
};
Best Practices
For Users
- Check balance before generating - Avoid failed requests
- Use daily check-ins - Free credit every day
- Choose providers wisely - Different costs for different quality
- Subscribe for better value - More credits per dollar
For Developers
- Always check balance before allowing actions
- Deduct only on success - Don't charge for failures
- Log all transactions - Important for debugging
- Set appropriate costs - Balance user value and server costs
- Monitor usage - Detect abuse or unusual patterns
Customization
Adjust Credit Costs
Edit src/services/constant.ts:
export const AI_GENERATION_CREDITS = {
image: {
openai: 10, // Increase DALL-E cost to 10
replicate: 5, // Increase Replicate to 5
},
video: {
seedance: 15 // Increase Seedance to 15
},
};
Adjust New User Bonus
export const CreditsAmount = {
NewUserGet: 500, // Give 500 credits instead of 100
};
Custom Expiration
// Give credits that expire in 30 days
const expiry = new Date();
expiry.setDate(expiry.getDate() + 30);
await increaseCredits({
user_uuid,
trans_type: CreditsTransType.SystemAdd,
credits: 1000,
expired_at: expiry.toISOString(),
});
Analytics
Credit Usage Reports
Query credit usage:
-- Total credits consumed by AI generation
SELECT SUM(ABS(credits)) as total_used
FROM credits_trans
WHERE trans_type = 'ai_generation'
AND created_at > NOW() - INTERVAL '30 days';
-- Credits by AI type
SELECT ai_type, provider, SUM(credits_cost) as total_cost
FROM generations
WHERE status = 'completed'
GROUP BY ai_type, provider;
User Spending Analysis
// Get top credit spenders
const topSpenders = await db()
.select({
user_uuid: credits.user_uuid,
total_spent: sql`SUM(ABS(credits))`,
})
.from(credits_trans)
.where(eq(credits_trans.trans_type, "ai_generation"))
.groupBy(credits.user_uuid)
.orderBy(desc(sql`SUM(ABS(credits))`))
.limit(10);
Next Steps
- AI Generator Documentation - How to use credits
- Payment System - Buy more credits
- Referral Program - Earn credits
- API Reference - Credit APIs