Files
urukprize/backend/app/Controllers/SubscriptionController.php

254 lines
10 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Controllers;
use Core\Request;
use Core\Response;
use Core\Security;
use Core\Database;
use App\Services\SuperQiParserService;
use PDO;
class SubscriptionController
{
/**
* GET /api/v1/subscription/plans
*/
public function getPlans(Request $request): void
{
$appConfig = require __DIR__ . '/../../config/app.php';
$plans = [
[
'id' => '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.');
}
}