🐛 Bug Analizi 9 Ocak 2026

Premium Üyelik Durumu Bug Analizi

Şarkı çaldığında premium üyelik durumunun ücretsiz üyeye dönmesi sorunu

📝 Basit Anlatım (Herkes İçin)

Problem: Sayfa açıldığında "Premium Üye" olarak görünüyorsunuz ama bir şarkıya tıklayıp çaldığınızda "Ücretsiz Üye" olarak görünmeye başlıyorsunuz.

Neden Oluyor: Sayfa ilk yüklendiğinde sistem veritabanını doğru kontrol ediyor. Ama şarkı çaldığında, backend (sunucu) eski bir bilgiyle cevap veriyor ve bu eski bilgi "premium değil" diyor.

Sonuç: Siz aslında premium üyesiniz ama ekranda "Premium'a Geç" butonu çıkıyor. Crown (taç) ikonu kayboluyor, yerine sadece isminizin ilk harfi görünüyor.

⚠️ Önemli: Gerçek premium durumunuz değişmiyor! Sadece ekrandaki görüntü yanlış. Şarkılar çalmaya devam ediyor çünkü asıl kontrol doğru çalışıyor.

🔧 Teknik Detaylar (Geliştiriciler İçin)

🔄 Bug Akışı

1. Sayfa yüklenir → PHP: $user->isPremiumOrTrial() → TRUE ✅

2. JavaScript: currentUser.is_premium = true → Premium Üye görünür ✅

3. Şarkı tıklanır → API: /api/muzibu/songs/{id}/stream

4. Backend: getSubscriptionData() → is_premium: false döner ❌

5. JavaScript: currentUser.is_premium = false → Ücretsiz Üye görünür ❌

🎯 Root Cause (Ana Neden)

SongStreamController.php:512-548 - getSubscriptionData() metodu:

// 🔴 PROBLEM: Sadece model değerine bakıyor
$expiresAt = $user->subscription_expires_at;
$hasPremium = $expiresAt && $expiresAt->isFuture();

if (!$hasPremium) {
    return ['is_premium' => false, ...];  // ❌ YANLIŞ SONUÇ
}

Model stale (eski) ise veya cache'den yüklenmişse, yanlış değer döndürür.

📊 Karşılaştırma

✅ Doğru Çalışan (User Model)

app/Models/User.php:379-416

isPremium() {
  // 1. Model değeri kontrol
  if ($this->subscription_expires_at->isFuture())
    return true;

  // 2. FRESH DB kontrolü
  $tenantExpiry = DB::table('users')
    ->where('id', $this->id)
    ->value('subscription_expires_at');

  if ($tenantExpiry && Carbon::parse($tenantExpiry)->isFuture())
    return true;

  // 3. Subscriptions tablosu fallback
  // ...
}

❌ Yanlış Çalışan (API)

SongStreamController.php:512-548

getSubscriptionData($user) {
  // ❌ SADECE model değerine bakıyor
  $expiresAt = $user->subscription_expires_at;
  $hasPremium = $expiresAt && $expiresAt->isFuture();

  // Fresh DB kontrolü YOK!
  // Subscriptions fallback YOK!

  if (!$hasPremium) {
    return ['is_premium' => false];
  }
}

🖥️ JavaScript Tarafı

player-core.js:3207-3220

// 🔄 Her şarkı çalmada premium status güncellenir
if (this.currentUser) {
    if (streamData.is_premium !== undefined) {
        this.currentUser.is_premium = streamData.is_premium; // ← Backend false dönerse değişir!
    }
    // ...
}

Backend yanlış is_premium: false döndürdüğünde, JavaScript bu değeri alıp currentUser nesnesini güncelliyor.

📁 Etkilenen Dosyalar

💡 Çözüm Önerisi

SongStreamController.php - getSubscriptionData() Düzeltmesi

protected function getSubscriptionData($user): array
{
    if (!$user) {
        return [
            'is_premium' => false,
            'is_trial' => false,
            'trial_ends_at' => null,
            'subscription_ends_at' => null,
        ];
    }

    // 🔥 FIX: isPremiumOrTrial() metodunu kullan - fresh DB kontrolü yapar
    $isPremium = $user->isPremiumOrTrial();
    $isTrial = $user->isTrialActive();

    if (!$isPremium) {
        return [
            'is_premium' => false,
            'is_trial' => false,
            'trial_ends_at' => null,
            'subscription_ends_at' => null,
        ];
    }

    // 🔴 SINGLE SOURCE OF TRUTH: users.subscription_expires_at
    // Fresh değeri al (model stale olabilir)
    $expiresAt = \DB::table('users')
        ->where('id', $user->id)
        ->value('subscription_expires_at');

    $expiresAt = $expiresAt ? \Carbon\Carbon::parse($expiresAt) : null;

    // Trial kontrolü: Aktif trial subscription var mı?
    $trialSubscription = $user->subscriptions()
        ->where('status', 'trial')
        ->whereNotNull('trial_ends_at')
        ->where('trial_ends_at', '>', now())
        ->first();

    return [
        'is_premium' => true,
        'is_trial' => $isTrial,
        'trial_ends_at' => $trialSubscription?->trial_ends_at?->toIso8601String(),
        'subscription_ends_at' => $expiresAt?->toIso8601String(),
    ];
}

⚠️ Alternatif Hızlı Çözüm

JavaScript tarafında backend'den gelen değeri görmezden gelmek:

// player-core.js:3207-3220
// 🔒 Backend değeri güvenilmez - sadece sayfa yüklendiğindeki değeri kullan
// if (streamData.is_premium !== undefined) {
//     this.currentUser.is_premium = streamData.is_premium;
// }
// YORUM SATIRI YAP veya SİL

Bu geçici bir çözüm. Asıl düzeltme backend'de yapılmalı.

🗄️ Veritabanı Kontrolü

Kullanıcının subscription_expires_at değerini kontrol edin:

SELECT id, name, email, subscription_expires_at
FROM tenant_muzibu_1528d0.users
WHERE email = 'KULLANICI_EMAIL';
  • subscription_expires_at gelecek tarihte olmalı
  • NULL veya geçmiş tarih ise → Premium değil

📋 Özet

Sorun Premium üyelik şarkı çalındığında ücretsiz üyeye dönüyor
Neden getSubscriptionData() metodu stale model değerine bakıyor
Etki UI yanlış gösteriliyor ama gerçek premium durumu değişmiyor
Çözüm getSubscriptionData() metodunda fresh DB kontrolü yap
Dosya Modules/Muzibu/app/Http/Controllers/Api/SongStreamController.php