🏗️ Mimari Plan v2 23 Aralık 2025

Subscription Zincir Sistemi

Ardışık subscription'lar ile üyelik süresi yönetimi

📝 Basit Anlatım (Herkes İçin)

Yeni Mantık Nasıl Çalışacak?

  • Her satın alma = Ayrı kayıt: Kullanıcı her paket aldığında subscriptions tablosuna yeni kayıt eklenir (bu doğru)
  • Kullanıcı tablosunda expires_at: Kullanıcının toplam üyelik bitiş tarihi burada tutulur
  • Zincir mantığı: Yeni subscription, bir öncekinin bittiği yerden başlar
  • Otomatik geçiş: Aktif subscription bitince, beklemedeki subscription otomatik aktif olur

📌 Örnek Senaryo:

1. Kullanıcı 1 aylık paket alıyor → Subscription #1 (Aktif: 23 Aralık - 23 Ocak)

2. 10 gün sonra 3 aylık paket alıyor → Subscription #2 (Beklemede: 23 Ocak - 23 Nisan)

3. 23 Ocak gelince → Subscription #1 "Süresi Doldu", Subscription #2 "Aktif" olur

4. users.expires_at = 23 Nisan 2026

🔄 Subscription Durumları ve Akışı

pending_payment

Ödeme Bekliyor

⏸️

pending

Beklemede

active

Aktif

🔴

expired

Süresi Doldu

pending_payment → (ödeme başarılı) → pending veya active → (süre dolunca) → expired

* Eğer ödeme anında aktif subscription yoksa → direkt "active"
* Eğer ödeme anında aktif subscription varsa → "pending" (sıraya girer)

🔧 Teknik Detaylar (Geliştiriciler İçin)

📊 Veritabanı Değişiklikleri

1. users tablosuna yeni sütun (Central DB):

subscription_expires_at DATETIME NULL
-- Kullanıcının toplam üyelik bitiş tarihi

2. subscriptions.status enum güncelleme:

ENUM('pending_payment', 'pending', 'active', 'expired', 'cancelled')

🛒 Yeni Satın Alma Akışı

CHECKOUT

Subscription oluştur (status: pending_payment)

started_at ve current_period_end henüz belirlenmez

CALLBACK

Ödeme başarılı → Tarihleri hesapla:

// Mevcut aktif/pending subscription var mı?

$lastSub = Subscription::where('user_id', $userId)

->whereIn('status', ['active', 'pending'])

->orderBy('current_period_end', 'desc')->first();


// Yeni subscription başlangıcı

$startDate = $lastSub ? $lastSub->current_period_end : now();

$endDate = $startDate->addDays($durationDays);


// Status belirleme

$status = $lastSub ? 'pending' : 'active';

UPDATE

users.subscription_expires_at güncelle

$user->subscription_expires_at = $endDate;

⏰ Scheduler / Cron Job

// Her saat çalışacak command

php artisan subscription:process-transitions


// 1. Süresi dolan active subscription'ları expired yap

Subscription::where('status', 'active')

->where('current_period_end', '<', now())

->update(['status' => 'expired']);


// 2. Sırası gelen pending subscription'ları active yap

Subscription::where('status', 'pending')

->where('current_period_start', '<=', now())

->update(['status' => 'active']);

🔐 Premium Erişim Kontrolü

// Hızlı kontrol (users tablosundan)

$isPremium = $user->subscription_expires_at > now();


// veya aktif subscription kontrolü

$hasActiveSubscription = $user->subscriptions()

->where('status', 'active')

->where('current_period_end', '>', now())

->exists();

💡 users.subscription_expires_at ile hızlı kontrol yapılabilir, subscription tablosuna sorgu gerekmez.

📊 Görsel Örnek: Zincir Subscription

ID Plan Başlangıç Bitiş Status Açıklama
101 1 Aylık 23 Ara 2025 23 Oca 2026 active Şu an kullanılıyor
102 3 Aylık 23 Oca 2026 23 Nis 2026 pending Sırada bekliyor
103 1 Yıllık 23 Nis 2026 23 Nis 2027 pending En son sırada

👤 users.subscription_expires_at = 23 Nisan 2027

Kullanıcının toplam üyelik süresi (tüm subscription'ların toplamı)

📋 Yapılacaklar Listesi

1. Migration
  • users tablosuna subscription_expires_at sütunu ekle (Central DB)
  • subscriptions.status enum güncelle (pending_payment, pending, active, expired, cancelled)
2. Model Güncelleme
  • User.php → subscription_expires_at cast, isPremium() helper
  • Subscription.php → Yeni status scope'ları (pending, expired)
3. Satın Alma Mantığı
  • Order::activateSubscriptionItems() güncelle:
    • Son subscription bitiş tarihini bul
    • Yeni subscription tarihlerini ona göre hesapla
    • Aktif subscription varsa "pending", yoksa "active" yap
    • users.subscription_expires_at güncelle
4. Scheduler Command
  • ProcessSubscriptionTransitions command oluştur
  • Kernel.php'ye saatlik schedule ekle
  • Active → Expired geçişi
  • Pending → Active geçişi
5. Mevcut Veri Düzeltme
  • Duplicate active subscription'ları düzelt (User 2 gibi)
  • users.subscription_expires_at değerlerini hesapla ve doldur

📁 Değişecek Dosyalar

Migration (Yeni):

database/migrations/xxxx_add_subscription_expires_at_to_users.php

database/migrations/tenant/xxxx_update_subscriptions_status_enum.php

Model:

app/Models/User.php → subscription_expires_at, isPremium()

Modules/Subscription/app/Models/Subscription.php → status scopes

Satın Alma:

Modules/Cart/app/Models/Order.php → activateSubscriptionItems()

Command (Yeni):

app/Console/Commands/ProcessSubscriptionTransitions.php

app/Console/Kernel.php → schedule