🎯 Subscription Event System

Ortak Dil - Tüm Olaylar İçin Tek Sistem

❌ Mevcut Durum: Dağınık Sistem

🎁 Trial Bitişi

Şu an: Cron job (günlük)

Sorun: Cron çalışmazsa bitmez!

⭐ Premium Bitişi

Şu an: isPremium() içinde tarih kontrolü

Sorun: Lazy check, status güncellenmez!

📱 Device Limit

Şu an: Giriş sırasında kontrol

Sorun: Eski cihaz otomatik çıkmaz!

💳 Ödeme Hatası

Şu an: Payment gateway callback

Sorun: Webhook gelmezse bilinmez!

⚠️ Sorun:
  • Her olay için farklı yöntem
  • Bazıları cron, bazıları lazy check
  • Hepsi farklı yerlerde kontrol ediliyor
  • Ortak mantık yok!

✅ Çözüm: Event-Based System (Ortak Dil)

🎯 Tek Prensip: Lazy Check + Event Dispatch

Tüm kontroller kullanılırken yapılır, event dispatch edilir!

📋 Nasıl Çalışır?

1

Kullanıcı Bir Şey Yapar

Müzik çalar, giriş yapar, sayfa açar...

2

Sistem Kontrolü (Lazy Check)

isPremium(), checkDeviceLimit(), vb.

3

Durum Değişikliği mi?

Trial bitti mi? Premium bitti mi? Limit aşıldı mı?

4

Event Dispatch!

TrialExpired, PremiumExpired, DeviceLimitExceeded

5

Listeners Çalışır

Email gönder, bildirim at, log tut, webhook tetikle

🎪 Tüm Subscription Olayları

Olay Tetikleyici Event Aksiyon
🎁 Trial Bitişi isPremium() çağrısı TrialExpired Status → expired, Email, Bildirim
⭐ Premium Bitişi isPremium() çağrısı SubscriptionExpired Status → expired, Email, Banner
📱 Device Limit Giriş / Token refresh DeviceLimitExceeded Eski cihazı çıkar, Bildirim
💳 Ödeme Hatası Webhook / Manual check PaymentFailed Kullanıcıya bildir, Retry
🔄 Otomatik Yenileme Cron (günlük) SubscriptionRenewed Ödeme al, Email, Extend
🚫 Manuel İptal Kullanıcı butonu SubscriptionCancelled Status → cancelled, Email
⚠️ Grace Period Ödeme hatası + 3 gün GracePeriodStarted Kullanıcıya uyarı, Retry

💻 Kod Örnekleri - Ortak Sistem

1️⃣ isPremium() - Trial & Premium Lazy Check

// app/Models/User.php public function isPremium(): bool { if (!$this->isMuzibuTenant()) { return false; } $subscription = $this->subscriptions() ->where('status', 'active') ->where('ends_at', '>', now()) ->first(); // 🔥 LAZY CHECK: Bitmiş subscription var mı? $expiredSubscription = $this->subscriptions() ->where('status', 'active') ->where('ends_at', '<=', now()) ->first(); if ($expiredSubscription) { // Status güncelle $expiredSubscription->update(['status' => 'expired']); // 🎉 EVENT DISPATCH! if ($expiredSubscription->plan->is_trial) { event(new \\App\\Events\\TrialExpired($this, $expiredSubscription)); } else { event(new \\App\\Events\\SubscriptionExpired($this, $expiredSubscription)); } } return $subscription ? true : false; }

2️⃣ Device Limit Check - Giriş Sırasında

// Modules/Muzibu/app/Services/DeviceService.php public function checkDeviceLimit(User $user): bool { $limit = $this->getDeviceLimit($user); $activeCount = $this->getActiveDeviceCount($user); // Limit aşıldı mı? if ($activeCount >= $limit) { // 🎉 EVENT DISPATCH! event(new \\App\\Events\\DeviceLimitExceeded($user, $activeCount, $limit)); // En eski cihazı çıkar (opsiyonel - listener'da da yapılabilir) $this->logoutOldestDevice($user); return false; } return true; }

3️⃣ Event Listener - Email & Bildirim

// app/Listeners/SendTrialExpiredNotification.php class SendTrialExpiredNotification { public function handle(TrialExpired $event) { $user = $event->user; $subscription = $event->subscription; // 1. Email gönder Mail::to($user->email) ->send(new TrialExpiredMail($user)); // 2. Push bildirim $user->notify(new TrialExpiredNotification()); // 3. Log Log::info("Trial expired", [ 'user_id' => $user->id, 'subscription_id' => $subscription->id, ]); // 4. Analytics Analytics::track('trial_expired', [ 'user_id' => $user->id, ]); } }

4️⃣ Cron Job - Yedek Temizlik (Hala Gerekli!)

// app/Console/Kernel.php protected function schedule(Schedule $schedule) { // YEDEK: Siteye hiç girmeyen kullanıcılar için toplu temizlik $schedule->call(function () { $expired = Subscription::where('status', 'active') ->where('ends_at', '<=', now()) ->get(); foreach ($expired as $subscription) { $subscription->update(['status' => 'expired']); // 🎉 EVENT DISPATCH! if ($subscription->plan->is_trial) { event(new TrialExpired($subscription->user, $subscription)); } else { event(new SubscriptionExpired($subscription->user, $subscription)); } } Log::info("Cron expired {$expired->count()} subscriptions"); })->daily(); }

📊 Eski vs Yeni Sistem

❌ Eski Sistem (Dağınık)

  • Trial → Cron job
  • Premium → Lazy check
  • Device → Giriş kontrolü
  • Payment → Webhook
  • Sorun: Her biri farklı!
  • Sorun: Email/bildirim mantığı dağınık
  • Sorun: Log tutma manuel

✅ Yeni Sistem (Event-Based)

  • Tüm kontroller → Lazy check
  • Durum değişince → Event dispatch
  • Event → Listener'lar çalışır
  • Email/bildirim → Listener'da
  • Artı: Ortak mantık!
  • Artı: Kolay genişletme
  • Artı: Test edilebilir

🎁 Event-Based Sistemin Avantajları

1. Ortak Dil

Tüm olaylar aynı mantıkla çalışır: Lazy check → Event → Listeners

2. Kolay Genişletme

Yeni listener ekle: Webhook, SMS, Analytics, vb.

3. Test Edilebilir

Event'ları mock'layarak test et, gerçek email göndermeden

4. Merkezi Log

Tüm olaylar aynı listener'dan geçer, tek yerden log

5. Async İşlem

Event'ları queue'ya at, arka planda işle (performans)

6. Cron Bağımsız

Cron çalışmasa bile lazy check devreye girer

📋 Tüm Event'lar

// app/Events/TrialExpired.php class TrialExpired { public $user; public $subscription; public function __construct(User $user, Subscription $subscription) { $this->user = $user; $this->subscription = $subscription; } } // app/Events/SubscriptionExpired.php class SubscriptionExpired { ... } // app/Events/DeviceLimitExceeded.php class DeviceLimitExceeded { public $user; public $currentCount; public $limit; } // app/Events/PaymentFailed.php class PaymentFailed { ... } // app/Events/SubscriptionRenewed.php class SubscriptionRenewed { ... } // app/Events/SubscriptionCancelled.php class SubscriptionCancelled { ... } // app/Events/GracePeriodStarted.php class GracePeriodStarted { ... }

🎧 Listener Örnekleri

Tek Event → Çoklu Listener

// app/Providers/EventServiceProvider.php protected $listen = [ TrialExpired::class => [ SendTrialExpiredEmail::class, // Email gönder SendTrialExpiredNotification::class, // Push bildirim LogTrialExpired::class, // Log tut TrackTrialExpiredAnalytics::class, // Analytics SendTrialExpiredWebhook::class, // Webhook (opsiyonel) ], DeviceLimitExceeded::class => [ LogoutOldestDevice::class, // En eski cihazı çıkar SendDeviceLimitNotification::class, // Bildirim gönder LogDeviceLimit::class, // Log ], SubscriptionExpired::class => [ SendSubscriptionExpiredEmail::class, UpdateUserPremiumStatus::class, // Cache temizle LogSubscriptionExpired::class, ], ];
💡 Avantaj:

Yeni bir listener eklemek istersen? Sadece array'e ekle! Kod değiştirmeden.

// Yeni listener ekle TrialExpired::class => [ SendTrialExpiredEmail::class, SendTrialExpiredNotification::class, SendTrialExpiredSMS::class, // 🆕 Yeni! SMS gönder ];

✅ SON KARAR - Ortak Sistem

🎯 Event-Based System FTW!

📜 Prensip:

  1. Lazy Check: Tüm kontroller kullanılırken (isPremium, checkDeviceLimit)
  2. Event Dispatch: Durum değişince event fırlat
  3. Listeners: Email, bildirim, log, analytics
  4. Cron (Yedek): Siteye girmeyen kullanıcılar için toplu temizlik

🎁 Sonuç:

  • ✅ Ortak dil (tüm olaylar aynı mantık)
  • ✅ Cron bağımsız (çalışmasa da sorun yok)
  • ✅ Kolay genişletme (yeni listener ekle)
  • ✅ Test edilebilir (event mock'la)
  • ✅ Merkezi log (tek yerden izle)
  • ✅ Async işlem (queue kullan)