ULTIMATE MASTER PLAN

FINAL

Subscription + Trial + Device + Premium - Tek Ortak Mantık

📅 2025-12-05 04:00 📊 10 Rapor Birleştirildi ✅ Tüm Kararlar Netleşti

❓ TÜM SORULAR + CEVAPLAR

S1: Trial Otomatik Başlar mı?

✅ EVET - Kullanıcı üye olunca otomatik başlatılır!

Sebep: Friction azaltma, yüksek conversion

Flow: Kayıt → Trial subscription oluştur → Status: trial

Alternatif YOK: Kullanıcı seçmez, direkt trial verilir

S2: Trial Süresi Kaç Gün? Dinamik mi?

✅ 7 Gün - Trial Planının Cycle Süresinden Alınır

Kaynak: Trial planının tek cycle'ı (7 gün, ₺0.00)

Avantaj: Tek kaynak gerçeği (DRY prensibi)

NOT: Settings'den ALINMAZ (senkronizasyon sorunu olur)

$trialPlan = SubscriptionPlan::where('is_trial', true)->first();
$trialDays = $trialPlan->billing_cycles['7-gunluk']['duration_days']; // 7

S3: Trial Planı Nasıl İşaretlenir? Slug mu, Checkbox mu?

✅ CHECKBOX - is_trial (boolean field)

❌ Slug Yaklaşımı (Reddedildi)

  • • Typo riski (tirall, traill)
  • • Slug değiştirilebilir
  • • Belirsiz

✅ Checkbox Yaklaşımı (Kabul)

  • • Net (TRUE/FALSE)
  • • Güvenli
  • • Validasyon kolay

Admin Panel:

☑️ Bu bir deneme üyeliğidir (is_trial)

⚠️ Validasyon: Sadece 1 plan is_trial=TRUE olabilir!

S4: Kullanıcı 1 Kere Trial Kullanmışsa Nasıl Anlarız?

✅ users.has_used_trial (boolean field)

Default: false

Trial başlatılınca: TRUE yapılır (ömür boyu)

Kontrol: Kayıt sırasında has_used_trial=TRUE ise trial YOK

if ($user->has_used_trial) {
    // Direkt free üye ol (trial yok)
} else {
    // Trial subscription oluştur
    $user->update(['has_used_trial' => true]);
}

S5: Trial Bitince Kullanıcı Ne Kadar Dinleme Yapacak?

✅ 30 SANİYE PREVIEW - Giriş/Çıkış BEKLENİR!

🔴 KRİTİK SORUN: Kullanıcı Çıkış Yapmaz!

  • • Kullanıcı 1 kere giriş yapar → 5 yıl çıkış yapmaz
  • • Token yenilenir (refresh token)
  • • Session expire olmaz
  • • Trial bitti ama kullanıcı hala sınırsız dinliyor!

✅ ÇÖZÜM: Request-Level Fresh Check

HER müzik stream isteğinde (cache YOK!):

$sub = Subscription::where('user_id', $userId)
    ->where('status', 'active')
    ->where('current_period_end', '>', now())
    ->first(); // CACHE YOK!

if (!$sub) return 30; // saniye

S6: Premium Bitince Kullanıcı Ne Kadar Dinleme Yapacak?

✅ AYNI MANTIK - 30 Saniye Preview

Trial ile aynı: Request-level fresh check

Giriş/çıkış beklenmez: Premium bittiği an → 30 saniye

Ortak kod: isPremium() veya hasActiveSubscription()

S7: Device Limit Dolunca Eski Device Çıkış Yapmadan Ne Olur?

✅ Request-Level Check + Event Dispatch

⚠️ SORUN:

  • • Telefon: Oturum açık (5 aydır çıkış yok)
  • • Tablet: Yeni giriş yaptı (limit: 2)
  • • İkisi de çalışıyor! (Limit aşıldı)

✅ ÇÖZÜM: Lazy Check + Event

// Her müzik isteğinde
$activeDevices = Session::where('user_id', $userId)->count();
if ($activeDevices > $plan->device_limit) {
    event(new DeviceLimitExceeded($user));
    return 30; // saniye
}

S8: Cron Çalışmazsa Trial/Premium Bitmiyor mu?

✅ HİBRİT SİSTEM - Lazy Check + Cron

1. Lazy Check (Ana Güvenlik)

Her istekte kontrol, cron çalışmasa bile güvenli

2. Cron (Yedek Güvenlik)

Günlük toplu temizlik, siteye girmeyen kullanıcılar için

// isPremium() içinde Lazy Check
if ($sub->ends_at < now()) {
    $sub->update(['status' => 'expired']);
    event(new SubscriptionExpired($user, $sub));
    return false;
}

🎯 ORTAK DİL: EVENT-BASED SİSTEM

Prensip: Lazy Check + Event Dispatch

📊 AKIŞ:

1 Kullanıcı bir şey yapar (müzik çalar, giriş yapar)
2 Sistem kontrolü (isPremium(), checkDeviceLimit())
3 Durum değişikliği mi? (trial bitti, premium bitti, device aşıldı)
4 EVENT DISPATCH!
5 Listeners çalışır (Email, bildirim, log, analytics)

🔔 EVENT'LAR:

TrialExpired

Trial süresi doldu

SubscriptionExpired

Premium bitişi

DeviceLimitExceeded

Device limit aşıldı

PaymentFailed

Ödeme başarısız

SubscriptionRenewed

Yenilendi

SubscriptionCancelled

İptal edildi

✅ AVANTAJLAR:

  • ✅ Ortak dil (tüm olaylar aynı mantık)
  • ✅ Kolay genişletme (yeni listener ekle)
  • ✅ Test edilebilir (event mock'la)
  • ✅ Merkezi log
  • ✅ Async işlem (queue)
  • ✅ Cron bağımsız

📊 DATABASE YAPISI (SON DURUM)

Central

subscription_plans: 18 field ✅

users.has_used_trial:

plans.is_trial:

Tenant İxtif

subscription_plans: 18 field ✅

users.has_used_trial:

plans.is_trial:

Tenant Muzibu

subscription_plans: 18 field ✅

users.has_used_trial:

plans.is_trial:

✅ HAZIR DURUM:

  • ✅ Migration unifikasyonu tamamlandı
  • ✅ currency_id kaldırıldı (migration + DB)
  • ✅ Tüm tenant'lar 18 field'de birleşti
  • ✅ Trial field'ları hazır (is_trial, trial_days, has_used_trial)

🎯 YAPILACAKLAR (SIRALAMA)

1

Trial Planı Oluştur (Admin Panel)

  • • Slug: trial (önemli değil, is_trial kullanılacak)
  • • ☑️ is_trial = TRUE (checkbox)
  • • Tek cycle: 7 gün, ₺0.00
  • • device_limit: 1
2

Subscription Service Güncelle

  • • createTrialSubscription() metodu ekle
  • • Kayıt sırasında has_used_trial kontrolü
  • • Trial bitince status = expired
3

Cache-Free Check Ekle (Stream Endpoint)

  • • Her müzik isteğinde fresh DB check
  • • Cache KULLANMA!
  • • Aktif subscription yoksa → 30 saniye
4

Event System Kur

  • • Event'ları oluştur (TrialExpired, SubscriptionExpired, vb.)
  • • Listener'ları oluştur (Email, Log, Analytics)
  • • Lazy check'lere event dispatch ekle
5

Cron Job Kur (Yedek Güvenlik)

  • • Günlük çalışacak
  • • Süresi dolan subscription'ları expired yap
  • • Event dispatch et
6

Test & QA

  • • Trial flow test (kayıt → trial → expired → 30 saniye)
  • • Premium flow test (satın al → aktif → expired → 30 saniye)
  • • Device limit test
  • • Event test (email, log kontrolü)

⚠️ KRİTİK NOTLAR

🔴 GİRİŞ/ÇIKIŞ BEKLENMEMELİ!

Kullanıcı 5 yıl çıkış yapmaz. Request-level fresh check ZORUNLU!

🟠 CACHE KULLANMA!

isPremium() her çağrıda fresh DB check yapmalı. Cache = Risk!

🟡 CRONSUZ DA ÇALIŞMALI!

Hibrit sistem (Lazy Check + Cron). Cron çalışmasa bile güvenli.

🟢 ORTAK MANTIK!

Trial, Premium, Device → Hepsi aynı Event-Based sistem!