إجمالي الفواتير
قيد المعالجة
تم الاعتماد
| اسم الشركة | الرقم الضريبي | رقم التسجيل | تاريخ الإضافة |
|---|---|---|---|
| الاسم | البريد الإلكتروني | الدور |
|---|---|---|
# Musadaq Project Documentation This file contains the complete source code of the project (excluding dependencies and sensitive data). ## File: `push.sh` ```sh #!/bin/bash # Get current timestamp TIMESTAMP=$(date "+%Y-%m-%d %H:%M:%S") echo "🚀 Starting Git Push Process..." echo "📅 Timestamp: $TIMESTAMP" # Add all changes git add . # Commit with timestamp git commit -m "Update: $TIMESTAMP" # Push to origin main explicitly git push origin main echo "✅ Done!" ``` ## File: `composer.json` ```json { "name": "musadaq/platform", "description": "Jordanian E-Invoicing Automation SaaS", "type": "project", "license": "proprietary", "require": { "php": ">=8.4", "ext-pdo": "*", "ext-pdo_mysql": "*", "ext-openssl": "*", "ext-sodium": "*", "ext-curl": "*", "ext-mbstring": "*", "ext-json": "*", "vlucas/phpdotenv": "^5.6", "monolog/monolog": "^3.5", "firebase/php-jwt": "^6.10", "ramsey/uuid": "^4.7", "nikic/fast-route": "^1.3", "predis/predis": "^2.2", "guzzlehttp/guzzle": "^7.9", "respect/validation": "^2.3", "league/flysystem": "^3.28", "symfony/mailer": "^7.1" }, "require-dev": { "phpunit/phpunit": "^11.0", "phpstan/phpstan": "^1.12", "squizlabs/php_codesniffer": "^3.10" }, "autoload": { "psr-4": { "App\\": "app/" } }, "config": { "optimize-autoloader": true, "sort-packages": true } } ``` ## File: `app/modules_app/auth/login.php` ```php 'required|email', 'password' => 'required' ]); if ($errors) { json_error('Validation Failed', 422, $errors); } $email = $data['email']; $password = $data['password']; // 2. DB Check (Using hash for lookup since email is encrypted) $db = Database::getInstance(); $emailHash = hash('sha256', strtolower($email)); $stmt = $db->prepare("SELECT * FROM users WHERE email_hash = ? LIMIT 1"); $stmt->execute([$emailHash]); $user = $stmt->fetch(); if (!$user || !password_verify($password, $user['password_hash'])) { json_error('بيانات الدخول غير صحيحة', 401); } // 3. Issue Token $secret = env('JWT_SECRET'); if (!$secret || strlen($secret) < 32) { error_log('FATAL: JWT_SECRET is missing or too short in .env'); json_error('Server configuration error', 500); } $payload = [ 'user_id' => $user['id'], 'tenant_id' => $user['tenant_id'], 'role' => $user['role'], 'exp' => time() + (15 * 60) // 15 minutes ]; $token = JWT::encode($payload, $secret); // 4. Update Refresh Token (Hashed before storage for security) $refreshToken = bin2hex(random_bytes(32)); $refreshTokenHash = hash('sha256', $refreshToken); $stmt = $db->prepare("UPDATE users SET refresh_token_hash = ? WHERE id = ?"); $stmt->execute([$refreshTokenHash, $user['id']]); json_success([ 'access_token' => $token, 'refresh_token' => $refreshToken, 'user' => [ 'id' => $user['id'], 'name' => (App\Core\Encryption::decrypt($user['name']) ?: $user['name']), 'email' => (App\Core\Encryption::decrypt($user['email']) ?: $user['email']) ] ], 'تم تسجيل الدخول بنجاح'); ``` ## File: `app/modules_app/auth/logout.php` ```php prepare("UPDATE users SET refresh_token_hash = NULL WHERE id = ?"); $stmt->execute([$userId]); json_success(null, 'تم تسجيل الخروج بنجاح'); ``` ## File: `app/modules_app/auth/refresh.php` ```php prepare("SELECT * FROM users WHERE refresh_token_hash = ? LIMIT 1"); $stmt->execute([$refreshTokenHash]); $user = $stmt->fetch(); if (!$user) { json_error('Invalid refresh token', 401); } $secret = env('JWT_SECRET'); if (!$secret || strlen($secret) < 32) { error_log('FATAL: JWT_SECRET is missing or too short in .env'); json_error('Server configuration error', 500); } $payload = [ 'user_id' => $user['id'], 'role' => $user['role'], 'exp' => time() + (15 * 60) ]; $newToken = JWT::encode($payload, $secret); $newRefreshToken = bin2hex(random_bytes(32)); $newRefreshTokenHash = hash('sha256', $newRefreshToken); $stmt = $db->prepare("UPDATE users SET refresh_token_hash = ? WHERE id = ?"); $stmt->execute([$newRefreshTokenHash, $user['id']]); json_success([ 'access_token' => $newToken, 'refresh_token' => $newRefreshToken ], 'تم تجديد الجلسة بنجاح'); ``` ## File: `app/modules_app/dashboard/stats.php` ```php query("SELECT COUNT(*) FROM invoices"); $total = $stmt->fetchColumn(); // Pending Invoices $stmt = $db->query("SELECT COUNT(*) FROM invoices WHERE status = 'pending'"); $pending = $stmt->fetchColumn(); // Approved Invoices $stmt = $db->query("SELECT COUNT(*) FROM invoices WHERE status = 'approved'"); $approved = $stmt->fetchColumn(); } catch (\Exception $e) { // Fallback if table doesn't exist yet $total = 0; $pending = 0; $approved = 0; } json_success([ 'total' => $total, 'pending' => $pending, 'approved' => $approved ]); ``` ## File: `app/modules_app/users/index.php` ```php prepare("SELECT id, name, email, role, is_active, created_at FROM users"); $stmt->execute(); $users = $stmt->fetchAll(); // 4. Decrypt sensitive data for the UI foreach ($users as &$user) { // Try to decrypt. If it fails (e.g. data was plain text), keep original. $decryptedName = Encryption::decrypt($user['name']); $user['name'] = $decryptedName !== false ? $decryptedName : $user['name']; $decryptedEmail = Encryption::decrypt($user['email']); $user['email'] = $decryptedEmail !== false ? $decryptedEmail : $user['email']; } json_success($users); ``` ## File: `app/modules_app/users/create.php` ```php 'required', 'email' => 'required|email', 'password' => 'required', 'role' => 'required' ]); if ($errors) { json_error('Validation Failed', 422, $errors); } $db = Database::getInstance(); // 3. Encrypt sensitive data $encryptedName = Encryption::encrypt($data['name']); $encryptedEmail = Encryption::encrypt($data['email']); $emailHash = hash('sha256', strtolower($data['email'])); // For fast lookup during login // 4. Save to Database try { $stmt = $db->prepare("INSERT INTO users (tenant_id, name, email, email_hash, password_hash, role, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)"); $stmt->execute([ $decoded['tenant_id'], $encryptedName, $encryptedEmail, $emailHash, password_hash($data['password'], PASSWORD_DEFAULT), $data['role'], date('Y-m-d H:i:s') ]); json_success(null, 'تم إضافة المستخدم بنجاح'); } catch (\Exception $e) { if (str_contains($e->getMessage(), 'Duplicate entry')) { json_error('البريد الإلكتروني مسجل مسبقاً', 409); } json_error('حدث خطأ أثناء حفظ البيانات', 500); } ``` ## File: `app/modules_app/companies/index.php` ```php query("SELECT * FROM companies WHERE deleted_at IS NULL"); } // 2. Admin sees all companies in their tenant else if ($decoded['role'] === 'admin') { $stmt = $db->prepare("SELECT * FROM companies WHERE tenant_id = ? AND deleted_at IS NULL"); $stmt->execute([$decoded['tenant_id']]); } // 3. Others (accountant, etc) see only their assigned company else { // Need to get their assigned company_id from users table first $stmtUser = $db->prepare("SELECT company_id FROM users WHERE id = ?"); $stmtUser->execute([$decoded['user_id']]); $assignedCompanyId = $stmtUser->fetchColumn(); $stmt = $db->prepare("SELECT * FROM companies WHERE id = ? AND deleted_at IS NULL"); $stmt->execute([$assignedCompanyId]); } $companies = $stmt->fetchAll(); // 3. Decrypt fields foreach ($companies as &$company) { // Decrypt Name $decryptedName = Encryption::decrypt($company['name']); $company['name'] = $decryptedName !== false ? $decryptedName : $company['name']; // Decrypt Name EN if (!empty($company['name_en'])) { $decryptedNameEn = Encryption::decrypt($company['name_en']); $company['name_en'] = $decryptedNameEn !== false ? $decryptedNameEn : $company['name_en']; } // Redact JoFotara secrets if returned to UI (or just don't return them) unset($company['jofotara_client_id_encrypted']); unset($company['jofotara_secret_key_encrypted']); unset($company['certificate_password_encrypted']); } json_success($companies); ``` ## File: `app/modules_app/companies/create.php` ```php 'required', 'tax_identification_number' => 'required' ]); if ($errors) { json_error('Validation Failed', 422, $errors); } $db = Database::getInstance(); try { $db->beginTransaction(); // 2. Encrypt sensitive fields $encryptedName = Encryption::encrypt($data['name']); $encryptedNameEn = !empty($data['name_en']) ? Encryption::encrypt($data['name_en']) : null; // Encrypt JoFotara keys if provided $jofotaraClientId = !empty($data['jofotara_client_id']) ? Encryption::encrypt($data['jofotara_client_id']) : null; $jofotaraSecretKey = !empty($data['jofotara_secret_key']) ? Encryption::encrypt($data['jofotara_secret_key']) : null; // 3. Save to Database $stmt = $db->prepare(" INSERT INTO companies ( tenant_id, name, name_en, tax_identification_number, commercial_registration_number, city, address, contact_email, contact_phone, jofotara_client_id_encrypted, jofotara_secret_key_encrypted, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "); $stmt->execute([ $decoded['user_id'], // Using current admin as tenant_id $encryptedName, $encryptedNameEn, $data['tax_identification_number'], $data['commercial_registration_number'] ?? null, $data['city'] ?? null, $data['address'] ?? null, $data['contact_email'] ?? null, $data['contact_phone'] ?? null, $jofotaraClientId, $jofotaraSecretKey, date('Y-m-d H:i:s') ]); $db->commit(); json_success(null, 'تم إنشاء الشركة بنجاح'); } catch (\Exception $e) { $db->rollBack(); json_error('حدث خطأ أثناء حفظ البيانات: ' . $e->getMessage(), 500); } ``` ## File: `app/middleware/HmacMiddleware.php` ```php $maxAgeSeconds) { json_error('Request expired. Check your system clock.', 401); } // 4. Build the expected signature $body = file_get_contents('php://input'); $payload = $timestamp . '.' . $body; $secret = env('HMAC_SECRET_KEY'); if (!$secret || strlen($secret) < 32) { error_log('FATAL: HMAC_SECRET_KEY is missing or too short in .env'); json_error('Server configuration error', 500); } // 5. Verify using constant-time comparison (prevents timing attacks) if (!Security::verifySignature($payload, $signature, $secret)) { error_log("HMAC verification failed for " . ($_SERVER['REQUEST_URI'] ?? '')); json_error('Invalid request signature', 401); } } } ``` ## File: `app/middleware/AuthMiddleware.php` ```php $ts > ($now - $timeWindow)) ); } } if (count($requests) >= $maxRequests) { flock($fp, LOCK_UN); fclose($fp); header('Retry-After: ' . $timeWindow); json_error('Too Many Requests. Please slow down.', 429); } // Record this request $requests[] = $now; // Write updated data back ftruncate($fp, 0); rewind($fp); fwrite($fp, json_encode($requests)); } finally { flock($fp, LOCK_UN); fclose($fp); } } } ``` ## File: `app/core/Validator.php` ```php $rule) { if (str_contains($rule, 'required') && (empty($data[$field]) && $data[$field] !== '0')) { $errors[$field] = "The {$field} field is required."; } if (str_contains($rule, 'email') && !empty($data[$field]) && !filter_var($data[$field], FILTER_VALIDATE_EMAIL)) { $errors[$field] = "The {$field} must be a valid email address."; } } return $errors; } } ``` ## File: `app/core/Encryption.php` ```php PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, PDO::ATTR_EMULATE_PREPARES => false, ]); } catch (PDOException $e) { http_response_code(500); header('Content-Type: application/json'); echo json_encode(['success' => false, 'message' => 'Database connection failed']); exit; } } return self::$instance; } } ``` ## File: `app/core/Security.php` ```php $value) { $data[$key] = self::sanitize($value); } } else if (is_string($data)) { $data = htmlspecialchars(strip_tags(trim($data)), ENT_QUOTES, 'UTF-8'); } return $data; } public static function generateRandomString(int $length = 64): string { return bin2hex(random_bytes($length / 2)); } public static function sign(string $data, string $secret): string { return hash_hmac('sha256', $data, $secret); } public static function verifySignature(string $data, string $signature, string $secret): bool { $expected = self::sign($data, $secret); return hash_equals($expected, $signature); } } ``` ## File: `app/core/JWT.php` ```php 'JWT', 'alg' => 'HS256']); $base64UrlHeader = self::base64UrlEncode($header); $base64UrlPayload = self::base64UrlEncode(json_encode($payload)); $signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $secret, true); $base64UrlSignature = self::base64UrlEncode($signature); return $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature; } public static function decode(string $token, string $secret): ?array { $parts = explode('.', $token); if (count($parts) !== 3) return null; [$header, $payload, $signature] = $parts; $expectedSignature = self::base64UrlEncode(hash_hmac('sha256', $header . "." . $payload, $secret, true)); if (!hash_equals($expectedSignature, $signature)) return null; $decodedPayload = json_decode(self::base64UrlDecode($payload), true); // Check expiry if (isset($decodedPayload['exp']) && $decodedPayload['exp'] < time()) return null; return $decodedPayload; } } ``` ## File: `app/bootstrap/init.php` ```php $success, 'data' => $data, // Return real data to client 'message' => $message, 'timestamp' => date('c') ], JSON_UNESCAPED_UNICODE); exit; } function json_error(string $message, int $code = 400, $errors = null) { json_response(false, $errors, $message, $code); } function json_success($data = null, ?string $message = 'Success', int $code = 200) { json_response(true, $data, $message, $code); } ``` ## File: `app/bootstrap/env.php` ```php $_ENV['DB_HOST'] ?? '127.0.0.1', 'port' => $_ENV['DB_PORT'] ?? '3306', 'database' => $_ENV['DB_DATABASE'] ?? 'musadaqDb', 'username' => $_ENV['DB_USERNAME'] ?? 'musadaqUser', 'password' => $_ENV['DB_PASSWORD'] ?? '', 'charset' => $_ENV['DB_CHARSET'] ?? 'utf8mb4', ]; ``` ## File: `app/helpers/helpers.php` ```php "; var_dump($v); echo ""; } die(); } } ``` ## File: `public/login.php` ```php
نظام أتمتة الفواتير الضريبية الذكي
ليس لديك حساب؟ ابدأ التجربة المجانية
إجمالي الفواتير
قيد المعالجة
تم الاعتماد
| اسم الشركة | الرقم الضريبي | رقم التسجيل | تاريخ الإضافة |
|---|---|---|---|
| الاسم | البريد الإلكتروني | الدور |
|---|---|---|
سجل شركتك الآن وابدأ أتمتة فواتيرك
لديك حساب بالفعل؟ تسجيل الدخول