feat: billing system — service plans, wallet, cost calculation

- Backend: billing module with ServicePlan, PricingRule, Wallet, WalletTransaction entities
- Admin can CRUD service plans with hourly/monthly/yearly billing cycles
- Each plan has flexible pricing rules (base_fee, cpu, memory, storage, db addon)
- Wallet system: auto-created per user, charge, deduct, refund, transaction history
- Cost calculation endpoint: cross-cycle conversion (hourly*720=monthly, monthly*12=yearly)
- Frontend: admin billing management page (/dashboard/admin/billing)
- Frontend: user wallet page with balance, quick-charge, transaction history
- Deploy page: cost breakdown shown in Review step (hourly/monthly/yearly)
- Navigation: Wallet link for users, Billing Plans link for admins
This commit is contained in:
keyhan
2026-04-07 01:45:45 +03:30
parent ac489c88d8
commit 4974f88e8c
16 changed files with 1302 additions and 3 deletions
+48
View File
@@ -238,3 +238,51 @@ export interface TicketStats {
avgResponseTimeMinutes: number;
byDepartment: Record<string, { total: number; open: number; answered: number; closed: number }>;
}
// ─── Billing types ──────────────────────────────────
export type BillingCycle = 'hourly' | 'monthly' | 'yearly';
export type PricingResourceType = 'base_fee' | 'cpu_per_core' | 'memory_per_gb' | 'storage_per_gb' | 'database_addon';
export type TransactionType = 'charge' | 'deduction' | 'refund';
export interface PricingRule {
id: string;
resourceType: PricingResourceType;
unitPrice: number;
description?: string;
planId: string;
createdAt: string;
}
export interface ServicePlan {
id: string;
name: string;
description?: string;
billingCycle: BillingCycle;
isActive: boolean;
pricingRules: PricingRule[];
createdAt: string;
updatedAt: string;
}
export interface WalletBalance {
balance: number;
}
export interface WalletTransaction {
id: string;
type: TransactionType;
amount: number;
balanceAfter: number;
description?: string;
applicationId?: string;
walletId: string;
createdAt: string;
}
export interface CostBreakdown {
hourly: number;
monthly: number;
yearly: number;
breakdown: { label: string; hourly: number; monthly: number; yearly: number }[];
}