'uruk-2year-standard', 'name' => 'عضوية جائزة أوروك التنفيذية (سنتان)', 'duration_years' => 2, 'price_usd' => (float)$appConfig['membership_price_usd'], 'price_iqd' => (float)$appConfig['membership_price_iqd'], 'benefits' => [ 'خصم 50% في شبكة المستشفيات والمراكز التخصصية في العراق والأردن ومصر ولبنان', 'خصومات فندقية وسياحية تتراوح بين 40% و60% في أرقى الفنادق الشريكة', 'وصول مجاني لأكثر من 30 تخصصاً تدريبياً معتمداً حضورياً وعبر المنصة الرقمية', 'شهادات معتمدة ومسجلة رسمياً باسم المشترك', 'هوية رقمية ذكية مع رمز QR مشفر للتحقق الفوري', ], 'official_activation_date' => '2026-01-01', 'early_registration_active' => true, ], ]; Response::success($plans, 'Available subscription plans retrieved.'); } /** * POST /api/v1/subscription/submit * User submits payment details (Method, Ref Number, Receipt). */ public function submitSubscription(Request $request): void { $userId = (int)$request->getHeader('x-user-id'); $method = strtoupper(trim((string)$request->get('method', 'SUPER_QI'))); $refNumber = trim((string)$request->get('reference_number')); $receiptUrl = trim((string)$request->get('receipt_url', '')); if (empty($refNumber)) { Response::error('Payment transaction reference number is required.'); } $appConfig = require __DIR__ . '/../../config/app.php'; $pdo = Database::getConnection(); // 1. Generate unique membership number (e.g. URUK-2026-XXXX) $memNumber = 'URUK-' . date('Y') . '-' . strtoupper(substr(uniqid(), -5)); $qrSeed = Security::generateRandomHex(16); $pdo->beginTransaction(); try { // Create subscription record with PENDING_PAYMENT $subStmt = $pdo->prepare(' INSERT INTO subscriptions (user_id, membership_number, plan_name, price_usd, price_local, currency, starts_at, expires_at, status, qr_seed, created_at) VALUES (:uid, :mem, "2-Year Executive Membership", :usd, :iqd, "IQD", CURDATE(), DATE_ADD(CURDATE(), INTERVAL 2 YEAR), "PENDING_PAYMENT", :seed, NOW()) '); $subStmt->execute([ ':uid' => $userId, ':mem' => $memNumber, ':usd' => $appConfig['membership_price_usd'], ':iqd' => $appConfig['membership_price_iqd'], ':seed' => $qrSeed, ]); $subscriptionId = (int)$pdo->lastInsertId(); // Create transaction record $transStmt = $pdo->prepare(' INSERT INTO transactions (subscription_id, user_id, method, reference_number, amount, currency, status, receipt_image_url, created_at) VALUES (:sub_id, :uid, :method, :ref, :amount, "IQD", "SUBMITTED", :receipt, NOW()) '); $transStmt->execute([ ':sub_id' => $subscriptionId, ':uid' => $userId, ':method' => $method, ':ref' => $refNumber, ':amount' => $appConfig['membership_price_iqd'], ':receipt' => $receiptUrl, ]); $pdo->commit(); // 2. Check if a matching SuperQi / ZainCash notification was already ingested by the Android listener $checkStmt = $pdo->prepare(' SELECT id, amount, status FROM transactions WHERE reference_number = :ref AND id != :current_id AND status = "VERIFIED_AUTO" LIMIT 1 '); $checkStmt->execute([':ref' => $refNumber, ':current_id' => (int)$pdo->lastInsertId()]); $preExistingPayment = $checkStmt->fetch(PDO::FETCH_ASSOC); if ($preExistingPayment) { // Instantly activate! $actSub = $pdo->prepare('UPDATE subscriptions SET status = "ACTIVE" WHERE id = :id'); $actSub->execute([':id' => $subscriptionId]); $actTrans = $pdo->prepare('UPDATE transactions SET status = "VERIFIED_AUTO", verified_at = NOW() WHERE subscription_id = :id'); $actTrans->execute([':id' => $subscriptionId]); Response::success([ 'subscription_id' => $subscriptionId, 'membership_number' => $memNumber, 'status' => 'ACTIVE', 'message' => 'تم التحقق من الحوالة آلياً بنجاح وتفعيل العضوية فورياً!', ], 'Membership activated automatically via SuperQi verification.'); } Response::success([ 'subscription_id' => $subscriptionId, 'membership_number' => $memNumber, 'status' => 'PENDING_PAYMENT', 'message' => 'تم استلام طلبك بنجاح وجارٍ التحقق من الحوالة وتفعيل بطاقتك.', ], 'Subscription request registered. Verification in progress.'); } catch (\Throwable $e) { $pdo->rollBack(); Response::error('Failed to submit subscription: ' . $e->getMessage(), 500); } } /** * GET /api/v1/subscription/my-card */ public function getMyCard(Request $request): void { $userId = (int)$request->getHeader('x-user-id'); $pdo = Database::getConnection(); $stmt = $pdo->prepare(' SELECT s.id, s.membership_number, s.plan_name, s.status, s.starts_at, s.expires_at, u.full_name, u.phone, u.avatar_url FROM subscriptions s JOIN users u ON u.id = s.user_id WHERE s.user_id = :uid ORDER BY s.id DESC LIMIT 1 '); $stmt->execute([':uid' => $userId]); $card = $stmt->fetch(PDO::FETCH_ASSOC); if (!$card) { Response::notFound('No subscription card found for this user.'); } Response::success($card, 'Digital card details retrieved.'); } /** * GET /api/v1/subscription/savings-summary * Returns total cash saved, breakdown by partner type, loyalty tier, and recent savings logs. */ public function getSavingsSummary(Request $request): void { $userId = (int)$request->getHeader('x-user-id'); $pdo = Database::getConnection(); // 1. Get totals $totStmt = $pdo->prepare(' SELECT COALESCE(SUM(saved_amount), 0) AS total_saved_iqd, COALESCE(SUM(paid_amount), 0) AS total_paid_iqd, COUNT(id) AS total_services_count FROM savings_ledger WHERE user_id = :uid '); $totStmt->execute([':uid' => $userId]); $totals = $totStmt->fetch(PDO::FETCH_ASSOC) ?: [ 'total_saved_iqd' => 0, 'total_paid_iqd' => 0, 'total_services_count' => 0, ]; $totalSaved = (float)$totals['total_saved_iqd']; $servicesCount = (int)$totals['total_services_count']; // 2. Breakdown by partner type (HOSPITAL, HOTEL, TRAINING_CENTER) $breakdownStmt = $pdo->prepare(' SELECT p.type, COUNT(s.id) AS count, COALESCE(SUM(s.saved_amount), 0) AS saved_iqd FROM savings_ledger s JOIN partners p ON p.id = s.partner_id WHERE s.user_id = :uid GROUP BY p.type '); $breakdownStmt->execute([':uid' => $userId]); $breakdownRows = $breakdownStmt->fetchAll(PDO::FETCH_ASSOC); $breakdown = [ 'HOSPITAL' => ['count' => 0, 'saved_iqd' => 0.0, 'label' => 'المستشفيات والعيادات'], 'HOTEL' => ['count' => 0, 'saved_iqd' => 0.0, 'label' => 'الفنادق والإقامة'], 'TRAINING_CENTER' => ['count' => 0, 'saved_iqd' => 0.0, 'label' => 'الدورات والورش الأكاديمية'], ]; foreach ($breakdownRows as $row) { $type = $row['type']; if (isset($breakdown[$type])) { $breakdown[$type]['count'] = (int)$row['count']; $breakdown[$type]['saved_iqd'] = (float)$row['saved_iqd']; } } // 3. Loyalty Tier Calculation (Gamification) if ($totalSaved >= 500000) { $tier = ['code' => 'DIAMOND', 'name_ar' => 'عضو ماسي أوروك', 'badge' => '💎', 'level' => 4]; } elseif ($totalSaved >= 250000) { $tier = ['code' => 'GOLD', 'name_ar' => 'عضو ذهبي ممتاز', 'badge' => '🥇', 'level' => 3]; } elseif ($totalSaved >= 100000) { $tier = ['code' => 'SILVER', 'name_ar' => 'عضو فضي متقدم', 'badge' => '🥈', 'level' => 2]; } else { $tier = ['code' => 'BRONZE', 'name_ar' => 'عضو برونزي', 'badge' => '🥉', 'level' => 1]; } // 4. Fetch last 10 logs $logsStmt = $pdo->prepare(' SELECT s.id, s.service_name, s.original_amount, s.discount_percentage, s.saved_amount, s.paid_amount, s.created_at, p.name_ar AS partner_name, p.type AS partner_type FROM savings_ledger s JOIN partners p ON p.id = s.partner_id WHERE s.user_id = :uid ORDER BY s.id DESC LIMIT 10 '); $logsStmt->execute([':uid' => $userId]); $recentLogs = $logsStmt->fetchAll(PDO::FETCH_ASSOC); Response::success([ 'total_saved_iqd' => $totalSaved, 'total_paid_iqd' => (float)$totals['total_paid_iqd'], 'total_services_count' => $servicesCount, 'breakdown' => $breakdown, 'tier' => $tier, 'recent_logs' => $recentLogs, ], 'Savings and gamification summary retrieved.'); } }