Version: 1.0
Last Updated: February 21, 2026
Projects: SkinPAI.API (Backend) | Skinpaimobile (Frontend)
SkinPAI is a comprehensive AI-powered skincare analysis platform that enables users to scan their skin, receive personalized analysis, get product recommendations, and connect with a skincare community.
| Feature | Description | User Tiers |
|---|---|---|
| Skin Scanning | AI-powered facial skin analysis using camera | All users |
| Analysis Results | Detailed skin health metrics (hydration, texture, concerns) | All users |
| Product Recommendations | Personalized product suggestions based on scan results | Member, Pro |
| Progress Tracking | Historical scan comparisons and progress charts | Member, Pro |
| Community Feed | Social features, posts, influencer stations | Member, Pro |
| Creator Studio | Influencer/creator station management | Pro only |
| Routines & Reminders | Skincare routine tracking with notifications | Member, Pro |
| Wallet System | In-app balance for subscriptions and purchases | All users |
┌─────────────────────────────────────────────────────────────────────┐
│ CLIENT LAYER │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ React + Vite Frontend │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
│ │ │ Components │ │ Services │ │ State/Context │ │ │
│ │ │ (UI/UX) │ │ (api.ts) │ │ (User, Theme, i18n)│ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
│ HTTP/REST (JSON)
│ JWT Authentication
▼
┌─────────────────────────────────────────────────────────────────────┐
│ API LAYER │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ .NET 9 Web API │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
│ │ │ Controllers │ │ Services │ │ Middleware │ │ │
│ │ │ (REST) │ │ (Business) │ │ (Auth, Logging) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
│ │ │ Repositories│ │ DTOs │ │ Entities │ │ │
│ │ │ (Data) │ │ (Transfer) │ │ (Domain) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
│ Entity Framework Core
▼
┌─────────────────────────────────────────────────────────────────────┐
│ DATA LAYER │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ SQL Server Database │ File Storage │ │
│ │ ┌────────────────────────┐ │ ┌─────────────────────┐ │ │
│ │ │ Users, Scans, Products │ │ │ /Uploads/ │ │ │
│ │ │ Subscriptions, Posts │ │ │ /profiles/ │ │ │
│ │ │ Routines, Chat, etc. │ │ │ /scans/ │ │ │
│ │ └────────────────────────┘ │ │ /posts/ │ │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
│ HTTP API
▼
┌─────────────────────────────────────────────────────────────────────┐
│ EXTERNAL SERVICES │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Hugging Face │ Payment Gateway │ Email Service │ │
│ │ (Skin Analysis) │ (Future) │ (Future) │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
SkinPAI.API/
├── Controllers/ # API endpoints (AuthController, ScansController, etc.)
├── Services/ # Business logic layer
├── Repositories/ # Data access layer (Unit of Work pattern)
├── Models/
│ ├── Entities/ # Database entity classes
│ └── DTOs/ # Data transfer objects
├── Data/
│ ├── SkinPAIDbContext # EF Core DbContext
│ └── DataSeeder.cs # Initial data seeding
├── Middleware/ # Custom middleware (logging, error handling)
├── Migrations/ # EF Core migrations
├── Uploads/ # File storage directory
├── Logs/ # Application logs
└── Program.cs # Application entry point
Skinpaimobile/src/
├── components/ # React components
│ ├── ui/ # Base UI components (shadcn/ui)
│ ├── figma/ # Figma-based design components
│ ├── navigation/ # Navigation components
│ ├── community/ # Community-specific components
│ └── shared/ # Shared utility components
├── services/
│ └── api.ts # API service layer
├── hooks/ # Custom React hooks
├── contexts/ # React context providers
├── types/ # TypeScript type definitions
├── data/ # Static data (legacy mock data)
├── i18n/ # Internationalization files
├── styles/ # CSS/styling files
└── utils/ # Utility functions
User Action → Component → api.ts → HTTP Request → Controller → Service → Repository → Database
↓
UI Update ← State Update ← Response ← DTO ← Entity Mapping
| Controller | Base Route | Description |
|---|---|---|
AuthController | /api/Auth | Authentication (login, register, tokens) |
UsersController | /api/Users | User profile management |
ScansController | /api/Scans | Skin scan operations |
ProductsController | /api/Products | Product catalog and favorites |
CommunityController | /api/Community | Posts, stations, campaigns |
RoutinesController | /api/Routines | Skincare routines and reminders |
SubscriptionsController | /api/Subscriptions | Plans, wallet, payments |
NotificationsController | /api/Notifications | User notifications |
ChatController | /api/Chat | Direct messaging |
// Service interfaces implemented:
IAuthService // Authentication, JWT token management
IUserService // User CRUD, profile updates
IScanService // Skin scan processing, analysis
IProductService // Product catalog, recommendations
ICommunityService // Posts, stations, campaigns
IRoutineService // Routines, reminders
ISubscriptionService // Plans, subscriptions, wallet
INotificationService // Push notifications, achievements
IChatService // Real-time messaging
IFileStorageService // File upload/download
ISkinAnalysisAIService // Hugging Face AI integration
The backend uses the Unit of Work pattern for data access:
public interface IUnitOfWork : IDisposable
{
IRepository<User> Users { get; }
IRepository<SkinScan> Scans { get; }
IRepository<Product> Products { get; }
IRepository<CommunityPost> CommunityPosts { get; }
// ... all repositories
Task<int> SaveChangesAsync();
}
User (1) ─────────────< (N) SkinScan
User (1) ─────────────< (N) UserSubscription
User (1) ─────────────< (N) UserRoutine
User (1) ─────────────< (N) CommunityPost
User (1) ─────────────< (N) UserProductFavorite
User (1) ─────────────< (N) Notification
User (1) ────────────── (1) SkinProfile
User (1) ────────────── (1) CreatorStation [optional]
SkinScan (1) ─────────── (1) SkinAnalysisResult
SkinScan (1) ─────────< (N) ProductRecommendation
Product (N) ─────────── (1) Brand
Product (N) ─────────── (1) Distributor
Product (N) ─────────── (1) ProductCategory
CreatorStation (1) ───< (N) StationFollower
CreatorStation (1) ───< (N) CommunityPost
CommunityPost (1) ────< (N) PostComment
CommunityPost (1) ────< (N) PostLike
// Custom middleware pipeline:
app.UseMiddleware<RequestLoggingMiddleware>(); // Logs all requests
app.UseMiddleware<ErrorHandlingMiddleware>(); // Global error handling
// Built-in middleware:
app.UseAuthentication(); // JWT validation
app.UseAuthorization(); // Role-based access
app.UseCors(); // Cross-origin requests
appsettings.json structure:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=SkinPAI;..."
},
"JwtSettings": {
"SecretKey": "...",
"Issuer": "SkinPAI.API",
"Audience": "SkinPAI.Client",
"AccessTokenExpirationMinutes": 60,
"RefreshTokenExpirationDays": 30
},
"HuggingFace": {
"ApiKey": "...",
"TimeoutSeconds": 60
},
"FileStorage": {
"BasePath": "./Uploads",
"MaxFileSizeMB": 10
}
}
| Component | Purpose | Key Features |
|---|---|---|
App.tsx | Root component | State management, routing, user session |
AuthScreen | Authentication | Login, register, social auth |
MemberDashboard | Main dashboard | Scan history, progress, quick actions |
ProDashboard | Pro user dashboard | Creator stats, analytics |
CameraInterface | Skin scanning | Camera capture, face detection |
ScanResults | Analysis display | Metrics, recommendations, comparison |
ProductRecommendations | Product catalog | Filters, search, favorites |
CommunityFeed | Social features | Posts, influencers, campaigns |
ProfileSettings | User settings | Profile, preferences, subscription |
Based on shadcn/ui with custom styling:
// Core UI components
Button, Card, Badge, Avatar, Input, Select,
Tabs, Dialog, Sheet, Checkbox, Slider, Progress,
DropdownMenu, ScrollArea, Separator, etc.
User State (Global):
interface User {
id: string;
type: 'guest' | 'member' | 'pro';
scansToday: number;
maxScans: number;
name?: string;
email?: string;
walletBalance?: number;
questionnaireCompleted?: boolean;
skinProfile?: SkinProfile;
}
State Persistence:
localStorage for user session and preferencesapi.ts Service Class:
class ApiService {
private baseUrl: string;
private accessToken: string | null;
// Authentication
async login(data: LoginRequest): Promise<ApiResponse<AuthResponse>>
async register(data: RegisterRequest): Promise<ApiResponse<AuthResponse>>
async socialLogin(data: SocialLoginRequest): Promise<ApiResponse<SocialAuthResponse>>
async refreshToken(token: string): Promise<ApiResponse<AuthResponse>>
// User Management
async getProfile(): Promise<ApiResponse<UserDto>>
async updateProfile(data: UpdateProfileRequest): Promise<ApiResponse<UserDto>>
// Scans
async uploadScan(imageBase64: string): Promise<ApiResponse<ScanResultDto>>
async getScanHistory(page: number): Promise<ApiResponse<PaginatedResponse<ScanResultDto>>>
async getSkinProgress(): Promise<ApiResponse<SkinProgressDto>>
// Products
async getProducts(params: ProductSearchParams): Promise<ApiResponse<PaginatedResponse<ProductDto>>>
async getFavorites(): Promise<ApiResponse<ProductDto[]>>
async addFavorite(productId: string): Promise<ApiResponse<void>>
// Community
async getCommunityFeed(page: number): Promise<ApiResponse<PaginatedResponse<CommunityPostDto>>>
async createPost(data: CreatePostRequest): Promise<ApiResponse<CommunityPostDto>>
async likePost(postId: string): Promise<ApiResponse<void>>
// ...more endpoints
}
export const api = new ApiService();
export default api;
Supported Languages:
Implementation:
// Using react-i18next
const { t, i18n } = useTranslation();
// RTL Context
const { isRTL, direction, flexDir } = useAppTranslation();
// Usage in components
<div className={`flex ${flexDir}`} style={{ direction }}>
<span>{t('dashboard.welcome')}</span>
</div>
| Hook | Purpose |
|---|---|
useAppTranslation | i18n + RTL handling |
useLocalStorage | Persisted state |
useNavigation | Screen navigation |
useScanProgress | Scan progress tracking |
Register a new user account.
Request:
{
"email": "user@example.com",
"password": "SecurePass123!",
"firstName": "Ahmed",
"lastName": "Hassan",
"phoneNumber": "+9647701234567",
"dateOfBirth": "1990-05-15",
"gender": "Male"
}
Response (200):
{
"userId": "guid",
"email": "user@example.com",
"firstName": "Ahmed",
"lastName": "Hassan",
"membershipType": "Guest",
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "base64-refresh-token",
"expiresAt": "2026-02-21T15:00:00Z"
}
Authenticate existing user.
Request:
{
"email": "user@example.com",
"password": "SecurePass123!"
}
Authenticate via social provider.
Request:
{
"provider": "google",
"idToken": "google-id-token",
"firstName": "Ahmed",
"lastName": "Hassan"
}
Create temporary guest session.
Response:
{
"userId": "guid",
"membershipType": "Guest",
"maxScans": 3,
"accessToken": "...",
"expiresAt": "..."
}
Refresh access token.
Request:
{
"refreshToken": "current-refresh-token"
}
Get current user profile.
Headers: Authorization: Bearer {token}
Response:
{
"userId": "guid",
"email": "user@example.com",
"firstName": "Ahmed",
"lastName": "Hassan",
"profileImageUrl": "/uploads/profiles/img.jpg",
"membershipType": "Member",
"membershipStatus": "Active",
"walletBalance": 50000.00,
"totalScansUsed": 15,
"isVerified": false,
"isCreator": false,
"questionnaireCompleted": true,
"skinProfile": {
"skinType": "Combination",
"skinConcerns": "Acne, Dark Spots",
"currentRoutine": "Basic cleanser and moisturizer",
"sunExposure": "Moderate",
"lifestyle": "Office work, occasional outdoor"
}
}
Update user profile.
Update skin questionnaire.
Request:
{
"skinType": "Oily",
"skinConcernsJson": "[\"Acne\", \"Large Pores\"]",
"currentRoutine": "Full 10-step routine",
"sunExposure": "High",
"lifestyle": "Active outdoor"
}
Upload profile image (multipart/form-data or base64).
Upload and analyze a skin scan.
Request:
{
"imageBase64": "data:image/jpeg;base64,/9j/4AAQSkZ...",
"scanType": "Face"
}
Response:
{
"scanId": "guid",
"userId": "guid",
"scanImageUrl": "/uploads/scans/scan_xxx.jpg",
"overlayImageUrl": "/uploads/scans/scan_xxx_overlay.jpg",
"scanType": "Face",
"scanDate": "2026-02-21T12:00:00Z",
"aiProcessingStatus": "Completed",
"overallScore": 78.5,
"estimatedSkinAge": 28,
"analysisResult": {
"hydrationLevel": 72,
"oilLevel": 45,
"textureScore": 68,
"poreVisibility": 35,
"wrinkleScore": 15,
"acneSeverity": 25,
"pigmentationScore": 30,
"sensitivityLevel": 20,
"skinTypeDetected": "Combination",
"recommendedIngredients": ["Niacinamide", "Salicylic Acid", "Hyaluronic Acid"],
"concerns": ["Moderate acne", "Visible pores"]
},
"recommendations": [/*product recommendations*/]
}
Get scan history (paginated).
Query Parameters:
page (default: 1)pageSize (default: 10)Get specific scan details.
Get most recent scan.
Get skin progress over time.
Response:
{
"currentScore": 78.5,
"scoreChange": +5.2,
"totalScans": 15,
"progressData": [
{"date": "2026-01-21", "value": 73.3},
{"date": "2026-02-21", "value": 78.5}
],
"improvements": ["Hydration +8%", "Acne -12%"],
"areasNeedingAttention": ["Sun protection"]
}
Get product catalog (paginated, filterable).
Query Parameters:
page, pageSizebrandId, categoryId, distributorIdminPrice, maxPriceskinType, skinConcerninStock, onSalesearchsortBy (price, rating, name)Response:
{
"items": [
{
"productId": "guid",
"productName": "Niacinamide 10% + Zinc 1%",
"description": "Targets blemishes and congestion",
"price": 15000.00,
"originalPrice": 18000.00,
"discountPercent": 17,
"averageRating": 4.8,
"totalReviews": 1250,
"productImageUrl": "/images/products/niacinamide.jpg",
"brand": {
"brandId": "guid",
"brandName": "The Ordinary",
"logoUrl": "/images/brands/ordinary.png",
"isVerified": true
},
"category": {
"categoryId": "guid",
"categoryName": "Serums",
"iconName": "droplet"
},
"distributor": {
"distributorId": "guid",
"name": "Basra Pharmacy",
"isPartner": true
},
"skinTypes": ["Oily", "Combination"],
"skinConcerns": ["Acne", "Blemishes"],
"keyIngredients": ["Niacinamide", "Zinc PCA"],
"inStock": true,
"shopUrl": "https://..."
}
],
"page": 1,
"pageSize": 20,
"totalCount": 156,
"totalPages": 8
}
Get single product details.
Get all brands.
Get all product categories.
Get all distributors.
Get user's favorite products.
Add product to favorites.
Remove product from favorites.
Get product bundles.
Get community posts (paginated).
Query Parameters:
page, pageSizestationId (filter by creator station)Create a new post.
Request:
{
"content": "Just finished my evening routine! ✨",
"postType": "image",
"title": "Evening Skincare",
"mediaBase64": ["data:image/jpeg;base64,..."],
"tags": ["routine", "evening"],
"hashtags": ["#skincare", "#glowup"]
}
Like a post.
Unlike a post.
Get post comments.
Add a comment.
Get all creator stations.
Get specific station.
Follow a station.
Get top influencers.
Get active brand campaigns.
Join a campaign.
Get community statistics.
Get user's routines.
Create a new routine.
Request:
{
"routineName": "Morning Routine",
"routineType": "AM",
"description": "My daily morning skincare",
"steps": [
{"stepNumber": 1, "productId": "guid", "productName": "Cleanser", "instructions": "Apply to wet face"},
{"stepNumber": 2, "productId": "guid", "productName": "Toner", "instructions": "Pat into skin"}
]
}
Get specific routine.
Update routine.
Delete routine.
Mark routine as completed for today.
Get routine reminders.
Create a reminder.
Update reminder.
Delete reminder.
Get available subscription plans.
Response:
[
{
"planId": "guid",
"planCode": "MEMBER",
"planName": "Member",
"description": "Essential skincare features",
"priceMonthly": 15000.00,
"priceYearly": 150000.00,
"scansPerDay": 5,
"hasAdvancedAnalysis": true,
"hasProductRecommendations": true,
"hasProgressTracking": true,
"hasCommunityAccess": true,
"hasCreatorStudio": false,
"hasPrioritySupport": false,
"adFree": false
},
{
"planId": "guid",
"planCode": "PRO",
"planName": "Pro",
"description": "Full creator features",
"priceMonthly": 35000.00,
"priceYearly": 350000.00,
"scansPerDay": -1,
"hasCreatorStudio": true,
"hasPrioritySupport": true,
"adFree": true
}
]
Subscribe to a plan.
Request:
{
"planId": "guid",
"paymentMethodId": "wallet"
}
Get current subscription.
Cancel subscription.
Get wallet balance.
Add funds to wallet.
Request:
{
"amount": 50000.00,
"paymentMethodId": "card_xxx"
}
Get user notifications.
Query Parameters:
unreadOnly (boolean)Mark notification as read.
Mark all notifications as read.
Get user achievements.
Get all conversations.
Get messages with a user.
Send a message.
Request:
{
"receiverId": "guid",
"content": "Hello!",
"mediaBase64": null
}
Mark conversation as read.
| Column | Type | Description |
|---|---|---|
| UserId | GUID (PK) | Primary key |
| NVARCHAR(256) | Unique email | |
| PasswordHash | NVARCHAR(MAX) | BCrypt hash |
| FirstName | NVARCHAR(100) | First name |
| LastName | NVARCHAR(100) | Last name |
| MembershipType | NVARCHAR(20) | Guest/Member/Pro |
| MembershipStatus | NVARCHAR(20) | Active/Suspended/Cancelled |
| WalletBalance | DECIMAL(18,2) | In-app balance |
| TotalScansUsed | INT | Lifetime scan count |
| IsCreator | BIT | Has creator station |
| IsVerified | BIT | Verified badge |
| CreatedAt | DATETIME2 | Registration date |
| LastLoginAt | DATETIME2 | Last login |
| Column | Type | Description |
|---|---|---|
| ScanId | GUID (PK) | Primary key |
| UserId | GUID (FK) | User reference |
| ScanImageUrl | NVARCHAR(500) | Image path |
| OverlayImageUrl | NVARCHAR(500) | AI overlay path |
| ScanType | NVARCHAR(50) | Face/Forehead/etc |
| AIProcessingStatus | NVARCHAR(20) | Pending/Completed/Failed |
| OverallScore | DECIMAL(5,2) | 0-100 score |
| EstimatedSkinAge | INT | AI-estimated age |
| ScanDate | DATETIME2 | Scan timestamp |
| Column | Type | Description |
|---|---|---|
| ResultId | GUID (PK) | Primary key |
| ScanId | GUID (FK) | Scan reference |
| HydrationLevel | DECIMAL(5,2) | 0-100 |
| OilLevel | DECIMAL(5,2) | 0-100 |
| TextureScore | DECIMAL(5,2) | 0-100 |
| AcneSeverity | DECIMAL(5,2) | 0-100 |
| WrinkleScore | DECIMAL(5,2) | 0-100 |
| PigmentationScore | DECIMAL(5,2) | 0-100 |
| SkinTypeDetected | NVARCHAR(50) | Detected skin type |
| ConcernsJson | NVARCHAR(MAX) | JSON array |
| RecommendedIngredientsJson | NVARCHAR(MAX) | JSON array |
| Column | Type | Description |
|---|---|---|
| ProductId | GUID (PK) | Primary key |
| ProductName | NVARCHAR(200) | Product name |
| BrandId | GUID (FK) | Brand reference |
| CategoryId | GUID (FK) | Category reference |
| DistributorId | GUID (FK) | Distributor reference |
| Price | DECIMAL(18,2) | Current price (IQD) |
| OriginalPrice | DECIMAL(18,2) | Original price |
| ProductImageUrl | NVARCHAR(500) | Image URL |
| ShopUrl | NVARCHAR(500) | Purchase URL |
| InStock | BIT | Availability |
| SkinTypesJson | NVARCHAR(MAX) | Target skin types |
| SkinConcernsJson | NVARCHAR(MAX) | Target concerns |
| KeyIngredientsJson | NVARCHAR(MAX) | Ingredients list |
| Column | Type | Description |
|---|---|---|
| PlanId | GUID (PK) | Primary key |
| PlanCode | NVARCHAR(50) | GUEST/MEMBER/PRO |
| PlanName | NVARCHAR(50) | Display name |
| PriceMonthly | DECIMAL(18,2) | Monthly price |
| PriceYearly | DECIMAL(18,2) | Yearly price |
| DailyScansLimit | INT | Scans per day (null=unlimited) |
| HasAdvancedAnalysis | BIT | Feature flag |
| HasProductRecommendations | BIT | Feature flag |
| HasProgressTracking | BIT | Feature flag |
| HasCommunityAccess | BIT | Feature flag |
| HasCreatorStudio | BIT | Feature flag |
┌─────────────────────────────────────────────────────────────────┐
│ SkinPAI Database │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Users │───────│ SkinScans │───────│ SkinAnalysis │ │
│ └──────────────┘ └──────────────┘ │ Results │ │
│ │ │ └──────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ UserRoutines │ │ Product │ │
│ └──────────────┘ │Recommendations│ │
│ │ └──────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │RoutineSteps │ │ Products │◄──────│ Brands │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌──────────────┐ │
│ └───────────────│ Distributors │ │
│ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Users │───────│CommunityPosts│───────│ PostComments │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌──────────────┐ │
│ │ │ PostLikes │ │
│ │ └──────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │CreatorStation│───────│StationFollows │ │BrandCampaigns│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Users │───────│ Subscriptions│───────│ Plans │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │WalletTransact│ │Notifications │ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
| Rule | Value |
|---|---|
| Scans per day | 3 |
| Scan history retention | 1 scan (latest only) |
| Product recommendations | Limited (3 products) |
| Community access | View only |
| Account persistence | 24 hours (unless registered) |
| Rule | Value |
|---|---|
| Scans per day | 5 |
| Scan history | Unlimited |
| Product recommendations | Full personalized |
| Progress tracking | Full access |
| Community access | Full (post, comment, like) |
| Routines | Up to 10 routines |
| Monthly price | 15,000 IQD |
| Yearly price | 150,000 IQD (17% savings) |
| Rule | Value |
|---|---|
| Scans per day | Unlimited |
| All Member features | Yes |
| Creator Studio | Full access |
| Station followers | Unlimited |
| Campaign participation | Priority |
| Priority support | Yes |
| Ad-free experience | Yes |
| Monthly price | 35,000 IQD |
| Yearly price | 350,000 IQD (17% savings) |
Daily Scan Limit Check
IF user.scansToday >= plan.dailyScansLimit THEN
REJECT "Daily scan limit reached"
Image Validation
AI Processing Flow
UPLOAD → VALIDATE → STORE → QUEUE_AI → PROCESS → STORE_RESULTS → NOTIFY
Score Calculation
overallScore = (hydration * 0.15) + (texture * 0.15) + (clarity * 0.15) +
(100 - acne * 0.2) + (100 - wrinkles * 0.15) + (elasticity * 0.1) +
(100 - pigmentation * 0.1)
Match by Skin Type
Match by Concerns
Match by Ingredients
Sort Order
Filtering Rules
Subscription Activation
Wallet Transactions
Cancellation
Upgrade/Downgrade
Posting Requirements
Creator Station Rules
Content Moderation
Engagement Limits
Routine Limits
Reminder Scheduling
Completion Tracking
# Clone and navigate
cd C:\Projects\SkinPAI.API
# Restore packages
dotnet restore
# Apply database migrations
cd SkinPAI.API
dotnet ef database update
# Run the API
dotnet run --urls="http://localhost:5001"
# Navigate to frontend
cd C:\Projects\Skinpaimobile-main\Skinpaimobile-main
# Install dependencies
npm install
# Start development server
npm run dev
Create/Update DTO (Models/DTOs/)
public record NewFeatureDto(
Guid Id,
string Name,
DateTime CreatedAt
);
Add Service Method (Services/)
public interface IFeatureService
{
Task<NewFeatureDto> GetFeatureAsync(Guid id);
}
Add Controller Endpoint (Controllers/)
[HttpGet("{id}")]
public async Task<ActionResult<NewFeatureDto>> GetFeature(Guid id)
{
var result = await _featureService.GetFeatureAsync(id);
return Ok(result);
}
Add Frontend API Method (services/api.ts)
async getFeature(id: string): Promise<ApiResponse<NewFeatureDto>> {
return this.request<NewFeatureDto>(`/Feature/${id}`);
}
Create Entity Class (Models/Entities/NewEntity.cs)
public class NewEntity
{
[Key]
public Guid Id { get; set; } = Guid.NewGuid();
[Required, MaxLength(200)]
public string Name { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
Add to DbContext (Data/SkinPAIDbContext.cs)
public DbSet<NewEntity> NewEntities { get; set; }
Add Repository (Repositories/IUnitOfWork.cs)
IRepository<NewEntity> NewEntities { get; }
Create Migration
dotnet ef migrations add AddNewEntity
dotnet ef database update
Create Component File (src/components/NewComponent.tsx)
import React from 'react';
import { Card, CardContent } from './ui/card';
import { useAppTranslation } from '../hooks/useAppTranslation';
interface NewComponentProps {
data: SomeType;
onAction: () => void;
}
export default function NewComponent({ data, onAction }: NewComponentProps) {
const { t, isRTL } = useAppTranslation();
return (
<Card>
<CardContent>
{/* Component content */}
</CardContent>
</Card>
);
}
Add to App.tsx (if needed for routing)
{currentScreen === 'new-feature' && (
<NewComponent
data={someData}
onAction={handleAction}
/>
)}
Add English Translation (src/i18n/en.json)
{
"newFeature": {
"title": "New Feature",
"description": "Description of feature"
}
}
Add Arabic Translation (src/i18n/ar.json)
{
"newFeature": {
"title": "ميزة جديدة",
"description": "وصف الميزة"
}
}
Use in Component
const { t } = useAppTranslation();
return <h1>{t('newFeature.title')}</h1>;
Check Logs
# View recent logs
Get-Content C:\Projects\SkinPAI.API\SkinPAI.API\Logs\SkinPAI-*.log -Tail 100
Test API Directly
# Health check
Invoke-RestMethod -Uri "http://localhost:5001/health"
# Test endpoint
Invoke-RestMethod -Uri "http://localhost:5001/api/Products?pageSize=5" `
-Headers @{Authorization = "Bearer $token"}
Check Swagger
http://localhost:5001/swaggerDebug Frontend API Calls
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=SkinPAI;..."
},
"JwtSettings": {
"SecretKey": "dev-secret-key-minimum-32-characters"
},
"HuggingFace": {
"ApiKey": "hf_xxxxxxxx"
}
}
# Development
dotnet run
# Production build
dotnet publish -c Release -o ./publish
# With Docker
docker build -t skinpai-api .
docker run -p 5001:80 skinpai-api
# Development
npm run dev
# Production build
npm run build
# Preview production build
npm run preview
# Create migration
dotnet ef migrations add MigrationName -p SkinPAI.API
# Apply migrations
dotnet ef database update -p SkinPAI.API
# Rollback
dotnet ef database update PreviousMigrationName -p SkinPAI.API
# Generate SQL script
dotnet ef migrations script -o migration.sql -p SkinPAI.API
| Code | Description |
|---|---|
| AUTH001 | Invalid credentials |
| AUTH002 | Account locked |
| AUTH003 | Token expired |
| AUTH004 | Refresh token invalid |
| SCAN001 | Daily limit reached |
| SCAN002 | Image validation failed |
| SCAN003 | AI processing failed |
| SUB001 | Insufficient wallet balance |
| SUB002 | Plan not found |
| COM001 | Content flagged |
| COM002 | Rate limit exceeded |
| HTTP Code | Usage |
|---|---|
| 200 | Success |
| 201 | Created |
| 204 | No Content (delete success) |
| 400 | Bad Request (validation) |
| 401 | Unauthorized |
| 403 | Forbidden (insufficient tier) |
| 404 | Not Found |
| 409 | Conflict (duplicate) |
| 429 | Rate Limited |
| 500 | Internal Server Error |
| Term | Definition |
|---|---|
| Scan | AI analysis of user's skin photo |
| Station | Creator's profile/channel (Pro only) |
| Routine | Ordered list of skincare products |
| Bundle | Curated product package |
| Campaign | Brand promotional event |
| IQD | Iraqi Dinar (currency) |
Document maintained by: SkinPAI Development Team
For questions: Contact development@skinpai.com
C#
100.0%
Version: 1.0
Last Updated: February 21, 2026
Projects: SkinPAI.API (Backend) | Skinpaimobile (Frontend)
SkinPAI is a comprehensive AI-powered skincare analysis platform that enables users to scan their skin, receive personalized analysis, get product recommendations, and connect with a skincare community.
| Feature | Description | User Tiers |
|---|---|---|
| Skin Scanning | AI-powered facial skin analysis using camera | All users |
| Analysis Results | Detailed skin health metrics (hydration, texture, concerns) | All users |
| Product Recommendations | Personalized product suggestions based on scan results | Member, Pro |
| Progress Tracking | Historical scan comparisons and progress charts | Member, Pro |
| Community Feed | Social features, posts, influencer stations | Member, Pro |
| Creator Studio | Influencer/creator station management | Pro only |
| Routines & Reminders | Skincare routine tracking with notifications | Member, Pro |
| Wallet System | In-app balance for subscriptions and purchases | All users |
┌─────────────────────────────────────────────────────────────────────┐
│ CLIENT LAYER │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ React + Vite Frontend │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
│ │ │ Components │ │ Services │ │ State/Context │ │ │
│ │ │ (UI/UX) │ │ (api.ts) │ │ (User, Theme, i18n)│ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
│ HTTP/REST (JSON)
│ JWT Authentication
▼
┌─────────────────────────────────────────────────────────────────────┐
│ API LAYER │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ .NET 9 Web API │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
│ │ │ Controllers │ │ Services │ │ Middleware │ │ │
│ │ │ (REST) │ │ (Business) │ │ (Auth, Logging) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │ │
│ │ │ Repositories│ │ DTOs │ │ Entities │ │ │
│ │ │ (Data) │ │ (Transfer) │ │ (Domain) │ │ │
│ │ └─────────────┘ └─────────────┘ └─────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
│ Entity Framework Core
▼
┌─────────────────────────────────────────────────────────────────────┐
│ DATA LAYER │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ SQL Server Database │ File Storage │ │
│ │ ┌────────────────────────┐ │ ┌─────────────────────┐ │ │
│ │ │ Users, Scans, Products │ │ │ /Uploads/ │ │ │
│ │ │ Subscriptions, Posts │ │ │ /profiles/ │ │ │
│ │ │ Routines, Chat, etc. │ │ │ /scans/ │ │ │
│ │ └────────────────────────┘ │ │ /posts/ │ │ │
│ └───────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
│ HTTP API
▼
┌─────────────────────────────────────────────────────────────────────┐
│ EXTERNAL SERVICES │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ Hugging Face │ Payment Gateway │ Email Service │ │
│ │ (Skin Analysis) │ (Future) │ (Future) │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
SkinPAI.API/
├── Controllers/ # API endpoints (AuthController, ScansController, etc.)
├── Services/ # Business logic layer
├── Repositories/ # Data access layer (Unit of Work pattern)
├── Models/
│ ├── Entities/ # Database entity classes
│ └── DTOs/ # Data transfer objects
├── Data/
│ ├── SkinPAIDbContext # EF Core DbContext
│ └── DataSeeder.cs # Initial data seeding
├── Middleware/ # Custom middleware (logging, error handling)
├── Migrations/ # EF Core migrations
├── Uploads/ # File storage directory
├── Logs/ # Application logs
└── Program.cs # Application entry point
Skinpaimobile/src/
├── components/ # React components
│ ├── ui/ # Base UI components (shadcn/ui)
│ ├── figma/ # Figma-based design components
│ ├── navigation/ # Navigation components
│ ├── community/ # Community-specific components
│ └── shared/ # Shared utility components
├── services/
│ └── api.ts # API service layer
├── hooks/ # Custom React hooks
├── contexts/ # React context providers
├── types/ # TypeScript type definitions
├── data/ # Static data (legacy mock data)
├── i18n/ # Internationalization files
├── styles/ # CSS/styling files
└── utils/ # Utility functions
User Action → Component → api.ts → HTTP Request → Controller → Service → Repository → Database
↓
UI Update ← State Update ← Response ← DTO ← Entity Mapping
| Controller | Base Route | Description |
|---|---|---|
AuthController | /api/Auth | Authentication (login, register, tokens) |
UsersController | /api/Users | User profile management |
ScansController | /api/Scans | Skin scan operations |
ProductsController | /api/Products | Product catalog and favorites |
CommunityController | /api/Community | Posts, stations, campaigns |
RoutinesController | /api/Routines | Skincare routines and reminders |
SubscriptionsController | /api/Subscriptions | Plans, wallet, payments |
NotificationsController | /api/Notifications | User notifications |
ChatController | /api/Chat | Direct messaging |
// Service interfaces implemented:
IAuthService // Authentication, JWT token management
IUserService // User CRUD, profile updates
IScanService // Skin scan processing, analysis
IProductService // Product catalog, recommendations
ICommunityService // Posts, stations, campaigns
IRoutineService // Routines, reminders
ISubscriptionService // Plans, subscriptions, wallet
INotificationService // Push notifications, achievements
IChatService // Real-time messaging
IFileStorageService // File upload/download
ISkinAnalysisAIService // Hugging Face AI integration
The backend uses the Unit of Work pattern for data access:
public interface IUnitOfWork : IDisposable
{
IRepository<User> Users { get; }
IRepository<SkinScan> Scans { get; }
IRepository<Product> Products { get; }
IRepository<CommunityPost> CommunityPosts { get; }
// ... all repositories
Task<int> SaveChangesAsync();
}
User (1) ─────────────< (N) SkinScan
User (1) ─────────────< (N) UserSubscription
User (1) ─────────────< (N) UserRoutine
User (1) ─────────────< (N) CommunityPost
User (1) ─────────────< (N) UserProductFavorite
User (1) ─────────────< (N) Notification
User (1) ────────────── (1) SkinProfile
User (1) ────────────── (1) CreatorStation [optional]
SkinScan (1) ─────────── (1) SkinAnalysisResult
SkinScan (1) ─────────< (N) ProductRecommendation
Product (N) ─────────── (1) Brand
Product (N) ─────────── (1) Distributor
Product (N) ─────────── (1) ProductCategory
CreatorStation (1) ───< (N) StationFollower
CreatorStation (1) ───< (N) CommunityPost
CommunityPost (1) ────< (N) PostComment
CommunityPost (1) ────< (N) PostLike
// Custom middleware pipeline:
app.UseMiddleware<RequestLoggingMiddleware>(); // Logs all requests
app.UseMiddleware<ErrorHandlingMiddleware>(); // Global error handling
// Built-in middleware:
app.UseAuthentication(); // JWT validation
app.UseAuthorization(); // Role-based access
app.UseCors(); // Cross-origin requests
appsettings.json structure:
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=SkinPAI;..."
},
"JwtSettings": {
"SecretKey": "...",
"Issuer": "SkinPAI.API",
"Audience": "SkinPAI.Client",
"AccessTokenExpirationMinutes": 60,
"RefreshTokenExpirationDays": 30
},
"HuggingFace": {
"ApiKey": "...",
"TimeoutSeconds": 60
},
"FileStorage": {
"BasePath": "./Uploads",
"MaxFileSizeMB": 10
}
}
| Component | Purpose | Key Features |
|---|---|---|
App.tsx | Root component | State management, routing, user session |
AuthScreen | Authentication | Login, register, social auth |
MemberDashboard | Main dashboard | Scan history, progress, quick actions |
ProDashboard | Pro user dashboard | Creator stats, analytics |
CameraInterface | Skin scanning | Camera capture, face detection |
ScanResults | Analysis display | Metrics, recommendations, comparison |
ProductRecommendations | Product catalog | Filters, search, favorites |
CommunityFeed | Social features | Posts, influencers, campaigns |
ProfileSettings | User settings | Profile, preferences, subscription |
Based on shadcn/ui with custom styling:
// Core UI components
Button, Card, Badge, Avatar, Input, Select,
Tabs, Dialog, Sheet, Checkbox, Slider, Progress,
DropdownMenu, ScrollArea, Separator, etc.
User State (Global):
interface User {
id: string;
type: 'guest' | 'member' | 'pro';
scansToday: number;
maxScans: number;
name?: string;
email?: string;
walletBalance?: number;
questionnaireCompleted?: boolean;
skinProfile?: SkinProfile;
}
State Persistence:
localStorage for user session and preferencesapi.ts Service Class:
class ApiService {
private baseUrl: string;
private accessToken: string | null;
// Authentication
async login(data: LoginRequest): Promise<ApiResponse<AuthResponse>>
async register(data: RegisterRequest): Promise<ApiResponse<AuthResponse>>
async socialLogin(data: SocialLoginRequest): Promise<ApiResponse<SocialAuthResponse>>
async refreshToken(token: string): Promise<ApiResponse<AuthResponse>>
// User Management
async getProfile(): Promise<ApiResponse<UserDto>>
async updateProfile(data: UpdateProfileRequest): Promise<ApiResponse<UserDto>>
// Scans
async uploadScan(imageBase64: string): Promise<ApiResponse<ScanResultDto>>
async getScanHistory(page: number): Promise<ApiResponse<PaginatedResponse<ScanResultDto>>>
async getSkinProgress(): Promise<ApiResponse<SkinProgressDto>>
// Products
async getProducts(params: ProductSearchParams): Promise<ApiResponse<PaginatedResponse<ProductDto>>>
async getFavorites(): Promise<ApiResponse<ProductDto[]>>
async addFavorite(productId: string): Promise<ApiResponse<void>>
// Community
async getCommunityFeed(page: number): Promise<ApiResponse<PaginatedResponse<CommunityPostDto>>>
async createPost(data: CreatePostRequest): Promise<ApiResponse<CommunityPostDto>>
async likePost(postId: string): Promise<ApiResponse<void>>
// ...more endpoints
}
export const api = new ApiService();
export default api;
Supported Languages:
Implementation:
// Using react-i18next
const { t, i18n } = useTranslation();
// RTL Context
const { isRTL, direction, flexDir } = useAppTranslation();
// Usage in components
<div className={`flex ${flexDir}`} style={{ direction }}>
<span>{t('dashboard.welcome')}</span>
</div>
| Hook | Purpose |
|---|---|
useAppTranslation | i18n + RTL handling |
useLocalStorage | Persisted state |
useNavigation | Screen navigation |
useScanProgress | Scan progress tracking |
Register a new user account.
Request:
{
"email": "user@example.com",
"password": "SecurePass123!",
"firstName": "Ahmed",
"lastName": "Hassan",
"phoneNumber": "+9647701234567",
"dateOfBirth": "1990-05-15",
"gender": "Male"
}
Response (200):
{
"userId": "guid",
"email": "user@example.com",
"firstName": "Ahmed",
"lastName": "Hassan",
"membershipType": "Guest",
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "base64-refresh-token",
"expiresAt": "2026-02-21T15:00:00Z"
}
Authenticate existing user.
Request:
{
"email": "user@example.com",
"password": "SecurePass123!"
}
Authenticate via social provider.
Request:
{
"provider": "google",
"idToken": "google-id-token",
"firstName": "Ahmed",
"lastName": "Hassan"
}
Create temporary guest session.
Response:
{
"userId": "guid",
"membershipType": "Guest",
"maxScans": 3,
"accessToken": "...",
"expiresAt": "..."
}
Refresh access token.
Request:
{
"refreshToken": "current-refresh-token"
}
Get current user profile.
Headers: Authorization: Bearer {token}
Response:
{
"userId": "guid",
"email": "user@example.com",
"firstName": "Ahmed",
"lastName": "Hassan",
"profileImageUrl": "/uploads/profiles/img.jpg",
"membershipType": "Member",
"membershipStatus": "Active",
"walletBalance": 50000.00,
"totalScansUsed": 15,
"isVerified": false,
"isCreator": false,
"questionnaireCompleted": true,
"skinProfile": {
"skinType": "Combination",
"skinConcerns": "Acne, Dark Spots",
"currentRoutine": "Basic cleanser and moisturizer",
"sunExposure": "Moderate",
"lifestyle": "Office work, occasional outdoor"
}
}
Update user profile.
Update skin questionnaire.
Request:
{
"skinType": "Oily",
"skinConcernsJson": "[\"Acne\", \"Large Pores\"]",
"currentRoutine": "Full 10-step routine",
"sunExposure": "High",
"lifestyle": "Active outdoor"
}
Upload profile image (multipart/form-data or base64).
Upload and analyze a skin scan.
Request:
{
"imageBase64": "data:image/jpeg;base64,/9j/4AAQSkZ...",
"scanType": "Face"
}
Response:
{
"scanId": "guid",
"userId": "guid",
"scanImageUrl": "/uploads/scans/scan_xxx.jpg",
"overlayImageUrl": "/uploads/scans/scan_xxx_overlay.jpg",
"scanType": "Face",
"scanDate": "2026-02-21T12:00:00Z",
"aiProcessingStatus": "Completed",
"overallScore": 78.5,
"estimatedSkinAge": 28,
"analysisResult": {
"hydrationLevel": 72,
"oilLevel": 45,
"textureScore": 68,
"poreVisibility": 35,
"wrinkleScore": 15,
"acneSeverity": 25,
"pigmentationScore": 30,
"sensitivityLevel": 20,
"skinTypeDetected": "Combination",
"recommendedIngredients": ["Niacinamide", "Salicylic Acid", "Hyaluronic Acid"],
"concerns": ["Moderate acne", "Visible pores"]
},
"recommendations": [/*product recommendations*/]
}
Get scan history (paginated).
Query Parameters:
page (default: 1)pageSize (default: 10)Get specific scan details.
Get most recent scan.
Get skin progress over time.
Response:
{
"currentScore": 78.5,
"scoreChange": +5.2,
"totalScans": 15,
"progressData": [
{"date": "2026-01-21", "value": 73.3},
{"date": "2026-02-21", "value": 78.5}
],
"improvements": ["Hydration +8%", "Acne -12%"],
"areasNeedingAttention": ["Sun protection"]
}
Get product catalog (paginated, filterable).
Query Parameters:
page, pageSizebrandId, categoryId, distributorIdminPrice, maxPriceskinType, skinConcerninStock, onSalesearchsortBy (price, rating, name)Response:
{
"items": [
{
"productId": "guid",
"productName": "Niacinamide 10% + Zinc 1%",
"description": "Targets blemishes and congestion",
"price": 15000.00,
"originalPrice": 18000.00,
"discountPercent": 17,
"averageRating": 4.8,
"totalReviews": 1250,
"productImageUrl": "/images/products/niacinamide.jpg",
"brand": {
"brandId": "guid",
"brandName": "The Ordinary",
"logoUrl": "/images/brands/ordinary.png",
"isVerified": true
},
"category": {
"categoryId": "guid",
"categoryName": "Serums",
"iconName": "droplet"
},
"distributor": {
"distributorId": "guid",
"name": "Basra Pharmacy",
"isPartner": true
},
"skinTypes": ["Oily", "Combination"],
"skinConcerns": ["Acne", "Blemishes"],
"keyIngredients": ["Niacinamide", "Zinc PCA"],
"inStock": true,
"shopUrl": "https://..."
}
],
"page": 1,
"pageSize": 20,
"totalCount": 156,
"totalPages": 8
}
Get single product details.
Get all brands.
Get all product categories.
Get all distributors.
Get user's favorite products.
Add product to favorites.
Remove product from favorites.
Get product bundles.
Get community posts (paginated).
Query Parameters:
page, pageSizestationId (filter by creator station)Create a new post.
Request:
{
"content": "Just finished my evening routine! ✨",
"postType": "image",
"title": "Evening Skincare",
"mediaBase64": ["data:image/jpeg;base64,..."],
"tags": ["routine", "evening"],
"hashtags": ["#skincare", "#glowup"]
}
Like a post.
Unlike a post.
Get post comments.
Add a comment.
Get all creator stations.
Get specific station.
Follow a station.
Get top influencers.
Get active brand campaigns.
Join a campaign.
Get community statistics.
Get user's routines.
Create a new routine.
Request:
{
"routineName": "Morning Routine",
"routineType": "AM",
"description": "My daily morning skincare",
"steps": [
{"stepNumber": 1, "productId": "guid", "productName": "Cleanser", "instructions": "Apply to wet face"},
{"stepNumber": 2, "productId": "guid", "productName": "Toner", "instructions": "Pat into skin"}
]
}
Get specific routine.
Update routine.
Delete routine.
Mark routine as completed for today.
Get routine reminders.
Create a reminder.
Update reminder.
Delete reminder.
Get available subscription plans.
Response:
[
{
"planId": "guid",
"planCode": "MEMBER",
"planName": "Member",
"description": "Essential skincare features",
"priceMonthly": 15000.00,
"priceYearly": 150000.00,
"scansPerDay": 5,
"hasAdvancedAnalysis": true,
"hasProductRecommendations": true,
"hasProgressTracking": true,
"hasCommunityAccess": true,
"hasCreatorStudio": false,
"hasPrioritySupport": false,
"adFree": false
},
{
"planId": "guid",
"planCode": "PRO",
"planName": "Pro",
"description": "Full creator features",
"priceMonthly": 35000.00,
"priceYearly": 350000.00,
"scansPerDay": -1,
"hasCreatorStudio": true,
"hasPrioritySupport": true,
"adFree": true
}
]
Subscribe to a plan.
Request:
{
"planId": "guid",
"paymentMethodId": "wallet"
}
Get current subscription.
Cancel subscription.
Get wallet balance.
Add funds to wallet.
Request:
{
"amount": 50000.00,
"paymentMethodId": "card_xxx"
}
Get user notifications.
Query Parameters:
unreadOnly (boolean)Mark notification as read.
Mark all notifications as read.
Get user achievements.
Get all conversations.
Get messages with a user.
Send a message.
Request:
{
"receiverId": "guid",
"content": "Hello!",
"mediaBase64": null
}
Mark conversation as read.
| Column | Type | Description |
|---|---|---|
| UserId | GUID (PK) | Primary key |
| NVARCHAR(256) | Unique email | |
| PasswordHash | NVARCHAR(MAX) | BCrypt hash |
| FirstName | NVARCHAR(100) | First name |
| LastName | NVARCHAR(100) | Last name |
| MembershipType | NVARCHAR(20) | Guest/Member/Pro |
| MembershipStatus | NVARCHAR(20) | Active/Suspended/Cancelled |
| WalletBalance | DECIMAL(18,2) | In-app balance |
| TotalScansUsed | INT | Lifetime scan count |
| IsCreator | BIT | Has creator station |
| IsVerified | BIT | Verified badge |
| CreatedAt | DATETIME2 | Registration date |
| LastLoginAt | DATETIME2 | Last login |
| Column | Type | Description |
|---|---|---|
| ScanId | GUID (PK) | Primary key |
| UserId | GUID (FK) | User reference |
| ScanImageUrl | NVARCHAR(500) | Image path |
| OverlayImageUrl | NVARCHAR(500) | AI overlay path |
| ScanType | NVARCHAR(50) | Face/Forehead/etc |
| AIProcessingStatus | NVARCHAR(20) | Pending/Completed/Failed |
| OverallScore | DECIMAL(5,2) | 0-100 score |
| EstimatedSkinAge | INT | AI-estimated age |
| ScanDate | DATETIME2 | Scan timestamp |
| Column | Type | Description |
|---|---|---|
| ResultId | GUID (PK) | Primary key |
| ScanId | GUID (FK) | Scan reference |
| HydrationLevel | DECIMAL(5,2) | 0-100 |
| OilLevel | DECIMAL(5,2) | 0-100 |
| TextureScore | DECIMAL(5,2) | 0-100 |
| AcneSeverity | DECIMAL(5,2) | 0-100 |
| WrinkleScore | DECIMAL(5,2) | 0-100 |
| PigmentationScore | DECIMAL(5,2) | 0-100 |
| SkinTypeDetected | NVARCHAR(50) | Detected skin type |
| ConcernsJson | NVARCHAR(MAX) | JSON array |
| RecommendedIngredientsJson | NVARCHAR(MAX) | JSON array |
| Column | Type | Description |
|---|---|---|
| ProductId | GUID (PK) | Primary key |
| ProductName | NVARCHAR(200) | Product name |
| BrandId | GUID (FK) | Brand reference |
| CategoryId | GUID (FK) | Category reference |
| DistributorId | GUID (FK) | Distributor reference |
| Price | DECIMAL(18,2) | Current price (IQD) |
| OriginalPrice | DECIMAL(18,2) | Original price |
| ProductImageUrl | NVARCHAR(500) | Image URL |
| ShopUrl | NVARCHAR(500) | Purchase URL |
| InStock | BIT | Availability |
| SkinTypesJson | NVARCHAR(MAX) | Target skin types |
| SkinConcernsJson | NVARCHAR(MAX) | Target concerns |
| KeyIngredientsJson | NVARCHAR(MAX) | Ingredients list |
| Column | Type | Description |
|---|---|---|
| PlanId | GUID (PK) | Primary key |
| PlanCode | NVARCHAR(50) | GUEST/MEMBER/PRO |
| PlanName | NVARCHAR(50) | Display name |
| PriceMonthly | DECIMAL(18,2) | Monthly price |
| PriceYearly | DECIMAL(18,2) | Yearly price |
| DailyScansLimit | INT | Scans per day (null=unlimited) |
| HasAdvancedAnalysis | BIT | Feature flag |
| HasProductRecommendations | BIT | Feature flag |
| HasProgressTracking | BIT | Feature flag |
| HasCommunityAccess | BIT | Feature flag |
| HasCreatorStudio | BIT | Feature flag |
┌─────────────────────────────────────────────────────────────────┐
│ SkinPAI Database │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Users │───────│ SkinScans │───────│ SkinAnalysis │ │
│ └──────────────┘ └──────────────┘ │ Results │ │
│ │ │ └──────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ UserRoutines │ │ Product │ │
│ └──────────────┘ │Recommendations│ │
│ │ └──────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │RoutineSteps │ │ Products │◄──────│ Brands │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌──────────────┐ │
│ └───────────────│ Distributors │ │
│ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Users │───────│CommunityPosts│───────│ PostComments │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │
│ │ ▼ │
│ │ ┌──────────────┐ │
│ │ │ PostLikes │ │
│ │ └──────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │CreatorStation│───────│StationFollows │ │BrandCampaigns│ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Users │───────│ Subscriptions│───────│ Plans │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │WalletTransact│ │Notifications │ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
| Rule | Value |
|---|---|
| Scans per day | 3 |
| Scan history retention | 1 scan (latest only) |
| Product recommendations | Limited (3 products) |
| Community access | View only |
| Account persistence | 24 hours (unless registered) |
| Rule | Value |
|---|---|
| Scans per day | 5 |
| Scan history | Unlimited |
| Product recommendations | Full personalized |
| Progress tracking | Full access |
| Community access | Full (post, comment, like) |
| Routines | Up to 10 routines |
| Monthly price | 15,000 IQD |
| Yearly price | 150,000 IQD (17% savings) |
| Rule | Value |
|---|---|
| Scans per day | Unlimited |
| All Member features | Yes |
| Creator Studio | Full access |
| Station followers | Unlimited |
| Campaign participation | Priority |
| Priority support | Yes |
| Ad-free experience | Yes |
| Monthly price | 35,000 IQD |
| Yearly price | 350,000 IQD (17% savings) |
Daily Scan Limit Check
IF user.scansToday >= plan.dailyScansLimit THEN
REJECT "Daily scan limit reached"
Image Validation
AI Processing Flow
UPLOAD → VALIDATE → STORE → QUEUE_AI → PROCESS → STORE_RESULTS → NOTIFY
Score Calculation
overallScore = (hydration * 0.15) + (texture * 0.15) + (clarity * 0.15) +
(100 - acne * 0.2) + (100 - wrinkles * 0.15) + (elasticity * 0.1) +
(100 - pigmentation * 0.1)
Match by Skin Type
Match by Concerns
Match by Ingredients
Sort Order
Filtering Rules
Subscription Activation
Wallet Transactions
Cancellation
Upgrade/Downgrade
Posting Requirements
Creator Station Rules
Content Moderation
Engagement Limits
Routine Limits
Reminder Scheduling
Completion Tracking
# Clone and navigate
cd C:\Projects\SkinPAI.API
# Restore packages
dotnet restore
# Apply database migrations
cd SkinPAI.API
dotnet ef database update
# Run the API
dotnet run --urls="http://localhost:5001"
# Navigate to frontend
cd C:\Projects\Skinpaimobile-main\Skinpaimobile-main
# Install dependencies
npm install
# Start development server
npm run dev
Create/Update DTO (Models/DTOs/)
public record NewFeatureDto(
Guid Id,
string Name,
DateTime CreatedAt
);
Add Service Method (Services/)
public interface IFeatureService
{
Task<NewFeatureDto> GetFeatureAsync(Guid id);
}
Add Controller Endpoint (Controllers/)
[HttpGet("{id}")]
public async Task<ActionResult<NewFeatureDto>> GetFeature(Guid id)
{
var result = await _featureService.GetFeatureAsync(id);
return Ok(result);
}
Add Frontend API Method (services/api.ts)
async getFeature(id: string): Promise<ApiResponse<NewFeatureDto>> {
return this.request<NewFeatureDto>(`/Feature/${id}`);
}
Create Entity Class (Models/Entities/NewEntity.cs)
public class NewEntity
{
[Key]
public Guid Id { get; set; } = Guid.NewGuid();
[Required, MaxLength(200)]
public string Name { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
Add to DbContext (Data/SkinPAIDbContext.cs)
public DbSet<NewEntity> NewEntities { get; set; }
Add Repository (Repositories/IUnitOfWork.cs)
IRepository<NewEntity> NewEntities { get; }
Create Migration
dotnet ef migrations add AddNewEntity
dotnet ef database update
Create Component File (src/components/NewComponent.tsx)
import React from 'react';
import { Card, CardContent } from './ui/card';
import { useAppTranslation } from '../hooks/useAppTranslation';
interface NewComponentProps {
data: SomeType;
onAction: () => void;
}
export default function NewComponent({ data, onAction }: NewComponentProps) {
const { t, isRTL } = useAppTranslation();
return (
<Card>
<CardContent>
{/* Component content */}
</CardContent>
</Card>
);
}
Add to App.tsx (if needed for routing)
{currentScreen === 'new-feature' && (
<NewComponent
data={someData}
onAction={handleAction}
/>
)}
Add English Translation (src/i18n/en.json)
{
"newFeature": {
"title": "New Feature",
"description": "Description of feature"
}
}
Add Arabic Translation (src/i18n/ar.json)
{
"newFeature": {
"title": "ميزة جديدة",
"description": "وصف الميزة"
}
}
Use in Component
const { t } = useAppTranslation();
return <h1>{t('newFeature.title')}</h1>;
Check Logs
# View recent logs
Get-Content C:\Projects\SkinPAI.API\SkinPAI.API\Logs\SkinPAI-*.log -Tail 100
Test API Directly
# Health check
Invoke-RestMethod -Uri "http://localhost:5001/health"
# Test endpoint
Invoke-RestMethod -Uri "http://localhost:5001/api/Products?pageSize=5" `
-Headers @{Authorization = "Bearer $token"}
Check Swagger
http://localhost:5001/swaggerDebug Frontend API Calls
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=SkinPAI;..."
},
"JwtSettings": {
"SecretKey": "dev-secret-key-minimum-32-characters"
},
"HuggingFace": {
"ApiKey": "hf_xxxxxxxx"
}
}
# Development
dotnet run
# Production build
dotnet publish -c Release -o ./publish
# With Docker
docker build -t skinpai-api .
docker run -p 5001:80 skinpai-api
# Development
npm run dev
# Production build
npm run build
# Preview production build
npm run preview
# Create migration
dotnet ef migrations add MigrationName -p SkinPAI.API
# Apply migrations
dotnet ef database update -p SkinPAI.API
# Rollback
dotnet ef database update PreviousMigrationName -p SkinPAI.API
# Generate SQL script
dotnet ef migrations script -o migration.sql -p SkinPAI.API
| Code | Description |
|---|---|
| AUTH001 | Invalid credentials |
| AUTH002 | Account locked |
| AUTH003 | Token expired |
| AUTH004 | Refresh token invalid |
| SCAN001 | Daily limit reached |
| SCAN002 | Image validation failed |
| SCAN003 | AI processing failed |
| SUB001 | Insufficient wallet balance |
| SUB002 | Plan not found |
| COM001 | Content flagged |
| COM002 | Rate limit exceeded |
| HTTP Code | Usage |
|---|---|
| 200 | Success |
| 201 | Created |
| 204 | No Content (delete success) |
| 400 | Bad Request (validation) |
| 401 | Unauthorized |
| 403 | Forbidden (insufficient tier) |
| 404 | Not Found |
| 409 | Conflict (duplicate) |
| 429 | Rate Limited |
| 500 | Internal Server Error |
| Term | Definition |
|---|---|
| Scan | AI analysis of user's skin photo |
| Station | Creator's profile/channel (Pro only) |
| Routine | Ordered list of skincare products |
| Bundle | Curated product package |
| Campaign | Brand promotional event |
| IQD | Iraqi Dinar (currency) |
Document maintained by: SkinPAI Development Team
For questions: Contact development@skinpai.com
C#
100.0%