Credits System#
Deduct Credits#
typescriptimport { deductCredits } from "@/modules/billing/credits.service"
const result = await deductCredits({
user_id: req.user!.user_id,
credits: 10,
description: "API call",
ref_type: "api_call",
ref_id: requestId,
})
if (!result.success) {
return sendError(res, 403, result.message) // "No available credits"
}
Deduction Flow#
vbnet1. Query user's available credit packs (ORDER BY priority ASC, expires_at ASC NULLS LAST)
↓ No packs → Return "No available credits"
2. Deduct by priority, record transactions to credit_transactions
3. If insufficient, remaining goes to overdraft (credit_overdrafts)
Priority#
Credit packs are consumed by priority ascending (lower number = consumed first). Same priority uses expiration date ascending (expiring sooner = consumed first).
Query Credits#
typescriptimport { getActiveCredits } from "@/modules/billing/credits.service"
const result = await getActiveCredits(user_id)
// result = { all: CreditPackage[], total: number }
Credit Sources#
| Source | mode | Description |
|---|---|---|
| Registration bonus | free | Auto-granted on signup |
| Subscription reward | subscription | Auto-credited on renewal |
| One-time purchase | one_time:credits | User purchases |
Related Tables#
credits— Credit accounts (one record per top-up/reward)credit_transactions— Transaction log (deductions/additions)credit_overdrafts— Overdraft records