Üyelik Sistemi - Final Uygulama Rehberi

Versiyon 12 | 2025-11-23 | Tenant-Aware Membership System

1. Özet & Sayılar

5
Tablo Rename
1
Yeni Modül
9
Yeni Kolon
7
Yeni Model
Neden Yeni Tablo Yok?

Mevcut shop_* tabloları hiç kullanılmamış (0 kayıt). Bunları rename edip kullanacağız.

  • user_devicessessions tablosu kullanılacak
  • login_logsactivity_log tablosu kullanılacak
  • customer_groupssubscription_plans yeterli

2. Mevcut Durum

Kullanılacak Mevcut Tablolar

Tablo Kayıt Durum Açıklama
shop_subscription_plans 0 RENAME → subscription_plans
shop_subscriptions 0 RENAME → subscriptions
shop_coupons 0 RENAME → coupons
shop_coupon_usages 0 RENAME → coupon_usages
shop_customer_addresses 0 RENAME → customer_addresses
sessions Var MEVCUT Cihaz takibi için kullanılacak
activity_log Var MEVCUT Giriş logları için kullanılacak

Mevcut Altyapı

Payment Modülü
  • Polymorphic Payable interface
  • PayTR entegrasyonu hazır
  • Transaction logging
  • Webhook handling
Spatie Activity Log
  • activity_log tablosu mevcut
  • Tüm modellerde trait var
  • Otomatik log kaydı
  • JSON metadata desteği

3. Kesinleşen Kararlar

Özellik Karar Not
Ödeme Sistemi PayTR Mevcut Payment modülü kullanılacak
Oturum Süresi 1 yıl SESSION_LIFETIME=525600
Fiyatlandırma 299₺/ay - 2.999₺/yıl Sadece Muzibu için
Cihaz Limiti Kullanıcı bazlı Varsayılan: 1 (settings'den)
Deneme Süresi Aboneliğe eklenir Kalan günler kaybolmaz
2FA SMS ile (opsiyonel) Tenant SMS API kullanır
Kurumsal Sınırsız alt hesap Sadece Muzibu
Kupon Universal sistem Tüm tenant'larda çalışır

4. Migrations

Önemli: Çifte Migration

Her migration hem database/migrations/ hem database/migrations/tenant/ altında oluşturulmalı!

Migration 1: Tablo Rename

2025_11_23_001_rename_shop_tables_to_universal.php

Eski İsim Yeni İsim Amaç
shop_subscription_plans subscription_plans Abonelik planları (Aylık, Yıllık, Kurumsal)
shop_subscriptions subscriptions Kullanıcı abonelikleri
shop_coupons coupons İndirim kuponları
shop_coupon_usages coupon_usages Kupon kullanım geçmişi
shop_customer_addresses customer_addresses Fatura adresleri

Migration 2: Users Tablosu Güncelleme

2025_11_23_002_add_membership_fields_to_users_table.php

Kolon Tip Varsayılan Açıklama
device_limit integer nullable null Kullanıcıya özel cihaz limiti
null ise settings'deki default_device_limit kullanılır
is_approved boolean true Manuel onay gerektiren üyelikler için
false ise kullanıcı giriş yapamaz, admin onayı bekler
failed_login_attempts integer 0 Başarısız giriş sayacı
Belirli sayıdan sonra hesap kilitlenir
locked_until timestamp nullable null Hesap kilit bitiş zamanı
Bu tarihten önce giriş engellidir
two_factor_enabled boolean false 2FA aktif mi?
true ise girişte SMS kodu istenir
two_factor_phone string nullable null 2FA telefon numarası
Varsayılan telefon yerine farklı numara kullanılabilir
is_corporate boolean false Kurumsal ana hesap mı?
true ise alt hesap oluşturabilir
corporate_code string nullable unique null Kurumsal davet kodu
Alt hesaplar bu kodla kayıt olur: FIRMA-ABC123
parent_user_id foreignId nullable null Üst kullanıcı referansı
Alt hesaplar için ana hesabın ID'si
// Migration kodu Schema::table('users', function (Blueprint $table) { $table->integer('device_limit')->nullable(); $table->boolean('is_approved')->default(true); $table->integer('failed_login_attempts')->default(0); $table->timestamp('locked_until')->nullable(); $table->boolean('two_factor_enabled')->default(false); $table->string('two_factor_phone')->nullable(); $table->boolean('is_corporate')->default(false); $table->string('corporate_code')->nullable()->unique(); $table->foreignId('parent_user_id')->nullable()->constrained('users')->onDelete('set null'); });

5. Models

Konum: app/Models/

SubscriptionPlan.php

Abonelik planları modeli

  • hasMany: subscriptions
  • Scope: active, forTenant
Subscription.php

Kullanıcı abonelikleri (implements Payable)

  • belongsTo: user, plan
  • Payable interface metodları
  • Scope: active, expired, trial
Coupon.php

İndirim kuponları

  • hasMany: usages
  • Scope: valid, expired
  • Method: isValidFor($user)
CouponUsage.php

Kupon kullanım geçmişi

  • belongsTo: user, coupon
  • morphTo: usable
CustomerAddress.php

Fatura adresleri

  • belongsTo: user
  • Scope: default

User Model Güncellemesi

app/Models/User.php

// Yeni relationships public function subscription() { return $this->hasOne(Subscription::class)->active(); } public function subscriptions() { return $this->hasMany(Subscription::class); } public function addresses() { return $this->hasMany(CustomerAddress::class); } public function parentUser() { return $this->belongsTo(User::class, 'parent_user_id'); } public function subUsers() { return $this->hasMany(User::class, 'parent_user_id'); } // Yeni methods public function getDeviceLimit(): int { return $this->device_limit ?? setting('default_device_limit', 1); } public function hasActiveSubscription(): bool { return $this->subscription() !== null; } public function isLocked(): bool { return $this->locked_until && $this->locked_until->isFuture(); }

6. Services

Konum: app/Services/Auth/

Service Amaç Ana Metodlar
DeviceService.php Cihaz yönetimi
  • getActiveSessions($user)
  • checkDeviceLimit($user)
  • terminateSession($sessionId)
  • terminateOldestSession($user)
TwoFactorService.php 2FA işlemleri
  • sendCode($user)
  • verifyCode($user, $code)
  • enable($user, $phone)
  • disable($user)
SubscriptionService.php Abonelik işlemleri
  • create($user, $plan, $coupon)
  • renew($subscription)
  • cancel($subscription)
  • addTrialDays($subscription, $days)
CouponService.php Kupon işlemleri
  • validate($code, $user)
  • apply($coupon, $amount)
  • markAsUsed($coupon, $user, $model)
CorporateService.php Kurumsal hesap yönetimi
  • createSubUser($parent, $data)
  • sendInvite($parent, $email)
  • removeSubUser($parent, $subUser)
  • getSubUsers($parent)
LoginLogService.php Giriş log yönetimi
  • logSuccess($user, $request)
  • logFailure($email, $reason, $request)
  • getHistory($user)

7. Settings

Settings Grupları

SettingManagement modülünde yeni gruplar oluşturulacak. Admin panelden düzenlenebilir.

auth_registration (Kayıt Ayarları)

Key Tip Varsayılan Açıklama
registration_enabled boolean true Yeni kayıt açık mı?
require_email_verification boolean true E-posta doğrulama zorunlu mu?
require_approval boolean false Admin onayı gerekli mi?
trial_days integer 7 Ücretsiz deneme süresi

auth_session (Oturum Ayarları)

Key Tip Varsayılan Açıklama
session_lifetime integer 525600 Oturum süresi (dakika) - 1 yıl
default_device_limit integer 1 Varsayılan cihaz limiti

auth_security (Güvenlik Ayarları)

Key Tip Varsayılan Açıklama
max_login_attempts integer 5 Max başarısız giriş
lockout_duration integer 30 Kilitleme süresi (dakika)
two_factor_available boolean true 2FA kullanılabilir mi?
two_factor_code_expiry integer 5 2FA kod geçerlilik süresi (dk)

auth_subscription (Abonelik Ayarları)

Key Tip Varsayılan Açıklama
paid_membership_enabled boolean false Ücretli üyelik aktif mi?
auto_renewal_enabled boolean true Otomatik yenileme aktif mi?
renewal_reminder_days integer 7 Yenileme hatırlatma (gün önce)
grace_period_days integer 3 Ödeme tolerans süresi

corporate (Kurumsal Ayarlar)

Key Tip Varsayılan Açıklama
corporate_enabled boolean false Kurumsal üyelik aktif mi?
max_sub_users integer 0 Max alt kullanıcı (0=sınırsız)

8. Middleware

Konum: app/Http/Middleware/

Middleware Route Key Amaç
CheckDeviceLimit.php device.limit Cihaz limitini kontrol eder. Aşılmışsa eski cihazı çıkarma sayfasına yönlendirir.
CheckSubscription.php subscription Aktif abonelik kontrolü. Premium içerik için gerekli.
CheckApproval.php approved Kullanıcı onaylı mı kontrol eder. is_approved=false ise engeller.
// Kernel.php'ye ekle protected $middlewareAliases = [ // ... mevcut middleware'ler 'device.limit' => \App\Http\Middleware\CheckDeviceLimit::class, 'subscription' => \App\Http\Middleware\CheckSubscription::class, 'approved' => \App\Http\Middleware\CheckApproval::class, ];

9. Mail Module (nwidart)

Universal Mail Modülü

Tüm mail şablonları nwidart modül yapısında oluşturulacak. Tüm tenant'lar bu modülü kullanacak.

Modül Oluşturma

# nwidart modül oluştur php artisan module:make Mail # veya manuel olarak Modules/Mail/ altında oluştur

Modül Yapısı

Modules/Mail/ ├── app/ │ ├── Mail/ │ │ ├── WelcomeMail.php │ │ ├── TrialEndingMail.php │ │ ├── SubscriptionRenewalMail.php │ │ ├── PaymentSuccessMail.php │ │ ├── PaymentFailedMail.php │ │ ├── NewDeviceLoginMail.php │ │ ├── TwoFactorCodeMail.php │ │ └── CorporateInviteMail.php │ ├── Services/ │ │ └── MailService.php │ └── Providers/ │ └── MailServiceProvider.php ├── resources/ │ └── views/ │ └── emails/ │ ├── welcome.blade.php │ ├── trial-ending.blade.php │ ├── subscription-renewal.blade.php │ ├── payment-success.blade.php │ ├── payment-failed.blade.php │ ├── new-device-login.blade.php │ ├── two-factor-code.blade.php │ └── corporate-invite.blade.php ├── config/ │ └── config.php └── module.json

Mail Class'ları

Mail Class Tetikleyici İçerik
WelcomeMail.php Kayıt sonrası Hoş geldin mesajı, deneme süresi bilgisi, başlangıç rehberi
TrialEndingMail.php Deneme bitmeden 2 gün önce Hatırlatma, abone ol butonu, kalan gün
SubscriptionRenewalMail.php Yenileme öncesi 7 gün Otomatik yenileme bildirimi, tutar, tarih
PaymentSuccessMail.php Ödeme başarılı Ödeme onayı, fatura linki, sonraki yenileme
PaymentFailedMail.php Ödeme başarısız Hata bildirimi, tekrar deneme linki, destek
NewDeviceLoginMail.php Yeni cihazdan giriş Güvenlik uyarısı, cihaz bilgisi, tanımıyorsan bildir
TwoFactorCodeMail.php 2FA SMS yedeği 6 haneli kod, geçerlilik süresi
CorporateInviteMail.php Kurumsal davet Davet mesajı, şifre oluşturma linki

MailService.php

Modules/Mail/app/Services/MailService.php

namespace Modules\Mail\App\Services; class MailService { public function sendWelcome(User $user): void { Mail::to($user)->send(new WelcomeMail($user)); } public function sendTrialEnding(User $user, int $daysLeft): void { Mail::to($user)->send(new TrialEndingMail($user, $daysLeft)); } public function sendPaymentSuccess(User $user, Subscription $subscription): void { Mail::to($user)->send(new PaymentSuccessMail($user, $subscription)); } // ... diğer metodlar }

Kullanım

use Modules\Mail\App\Services\MailService; // Service injection ile public function register(MailService $mailService) { // Kullanıcı oluştur... $mailService->sendWelcome($user); } // veya facade ile \Modules\Mail\App\Mail\WelcomeMail::dispatch($user);

10. Cron Jobs

Konum: app/Console/Commands/

Command Schedule Görev
CheckTrialExpiryCommand.php Günlük 09:00
  • Deneme süresi bitenleri bul
  • 2 gün kalanları bul (hatırlatma)
  • E-posta gönder
SendRenewalRemindersCommand.php Günlük 10:00
  • 7 gün içinde yenilenecekleri bul
  • Hatırlatma e-postası gönder
ProcessRecurringPaymentsCommand.php Günlük 06:00
  • Bugün yenilenecekleri bul
  • Otomatik ödeme işle
  • Başarılı/başarısız e-posta
CleanupExpiredSessionsCommand.php Haftalık Pazar 03:00
  • Süresi dolmuş oturumları temizle
  • 1 yıldan eski aktivite loglarını arşivle
// app/Console/Kernel.php protected function schedule(Schedule $schedule) { $schedule->command('subscription:check-trial')->dailyAt('09:00'); $schedule->command('subscription:send-reminders')->dailyAt('10:00'); $schedule->command('subscription:process-recurring')->dailyAt('06:00'); $schedule->command('auth:cleanup-sessions')->weeklyOn(0, '03:00'); }

11. Uygulama Sırası

Önerilen Uygulama Sırası

Her adım tamamlandıktan sonra test edilmeli.

1 Migrations

2 Models

3 Settings

4 Services

5 Middleware

6 Mail Module (nwidart)

7 Cron Jobs

8 UI & Controllers

9 Test & Deploy

12. Tenant Farkları

Özellik İxtif (Tenant 2) Muzibu (Tenant 1001)
Ücretli Üyelik Kapalı Açık (299/2999₺)
Kurumsal Kapalı Açık (Sınırsız)
Deneme Süresi - 7 gün
2FA Opsiyonel Opsiyonel
Cihaz Limiti 1 1 (kurumsal: özel)
Oturum Süresi 1 yıl 1 yıl
Kupon Sistemi Aktif Aktif
Tenant Ayar Örnekleri

Her tenant kendi settings değerlerini kullanır:

// İxtif ayarları paid_membership_enabled = false corporate_enabled = false trial_days = 0 // Muzibu ayarları paid_membership_enabled = true corporate_enabled = true trial_days = 7