nodebb-plugin-pdf-secure 1.2.25 → 1.2.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/PRD (2).md ADDED
@@ -0,0 +1,844 @@
1
+ # PRD: IEU Forum Çok Katmanlı Premium Abonelik Sistemi
2
+
3
+ ## 1. Bağlam (Context)
4
+
5
+ ### Amaç
6
+
7
+ Sistemi 3 kademeli abonelik planına (Lite, Premium, VIP), aylık + yıllık ödeme seçeneklerine, plan yükseltme mekanizmasına, ve gelişmiş admin paneline dönüştürmek.
8
+
9
+ ---
10
+
11
+ ## 2. Abonelik Planları
12
+
13
+ ### 2.1 Plan Tanımları
14
+
15
+ | Plan | Aylık | Yıllık (8 ay bedava) | NodeBB Grubu | Seviye |
16
+ | ------------------------ | ----- | -------------------- | ------------ | ------ |
17
+ | **Lite** | 119₺ | 476₺ | `lite` | 1 |
18
+ | **Premium** (En Popüler) | 139₺ | 556₺ | `premium` | 2 |
19
+ | **VIP** (Elit) | 159₺ | 636₺ | `vip` | 3 |
20
+
21
+ ### 2.2 Plan Özellikleri
22
+
23
+ **Lite (Seviye 1)**
24
+
25
+ - Tüm çalışma sorularına sınırsız erişim
26
+ - En güncel ders notları arşivi
27
+
28
+ **Premium (Seviye 2) — Lite + ekstralar**
29
+
30
+ - Gelişmiş PDF önizleme ve araçları
31
+ - Akıllı CV Oluşturucu
32
+ - Hedef odaklı GPA Hesaplayıcı
33
+ - Sıfır Reklam (reklamsız arayüz)
34
+ - Niki Wallet: %30 indirimli kahve hediye
35
+ - Sosyal Analitik: Profil ve konu görüntüleyenleri görme
36
+
37
+ **VIP (Seviye 3) — Premium + ekstralar**
38
+
39
+ - Prestij: Profilde özel VIP Badge
40
+ - Öncülük: Yeni özelliklere erken erişim
41
+
42
+ ### 2.3 Ödeme Periyotları
43
+
44
+ - **Aylık**: Standart fiyat, 30 günlük süre
45
+ - **Yıllık**: 8 ay bedava (4 aylık ödemeyle 12 ay kullanım), 365 günlük süre
46
+
47
+ ---
48
+
49
+ ## 3. İş Kuralları
50
+
51
+ ### 3.1 Plan Yükseltme (Upgrade)
52
+
53
+ - Kullanıcı sadece **üst plana** geçebilir (Lite → Premium, Lite → VIP, Premium → VIP)
54
+ - **Prorated fark ücreti**: Kalan günlerin fiyat farkı hesaplanır
55
+ - Formül: `fark = (yeniPlanFiyat - eskiPlanFiyat) / toplamGün * kalanGün`
56
+ - Örnek: Lite'tan Premium'a, 15 gün kala: `(139-119)/30*15 = 10₺`
57
+ - Yükseltme sonrası bitiş tarihi değişmez, sadece plan seviyesi ve grup değişir
58
+ - Yıllık abonelikte de aynı prorated mantık geçerli
59
+
60
+ ### 3.2 Downgrade
61
+
62
+ - **Desteklenmiyor**. Kullanıcı alt plana geçemez
63
+ - Alt plan isteyen kullanıcı mevcut aboneliği iptal edip süre bittikten sonra yeni plan satın almalı
64
+
65
+ ### 3.3 Abonelik Süresi Dolduğunda
66
+
67
+ - Mevcut cron job (10dk) kontrol eder
68
+ - Kullanıcı ilgili NodeBB grubundan çıkarılır
69
+ - Subscription status "expired" olarak işaretlenir
70
+
71
+ ### 3.4 Yenileme
72
+
73
+ - **Otomatik yenileme varsayılan olarak açık** gelir (checkbox tikli)
74
+ - Kullanıcı istemezse satın alma sırasında tiki kaldırarak kapatabilir
75
+ - Otomatik yenileme tercihi subscription kaydında `autoRenew: true/false` olarak saklanır
76
+ - Abonelik süresi dolmadan önce (ör. 1 gün kala) cron job otomatik yenileme yapılacak abonelikleri tespit eder
77
+ - Otomatik yenileme açık olan kullanıcılar için aynı plan + period ile yeni ödeme talebi oluşturulur (PayTR tekrarlayan ödeme veya kayıtlı kart ile)
78
+ - Otomatik yenileme kapalıysa: kullanıcı manuel olarak tekrar ödeme yaparak süre uzatır
79
+ - Aynı plan ile uzatma: mevcut bitiş tarihine süre eklenir
80
+
81
+ ### 3.5 Kullanıcı Abonelik İptali — IP Kısıtlamalı Endpoint
82
+
83
+ NodeBB tarafında **herhangi bir plugin yazılmaz** ve geçici grup mekanizması kullanılmaz. Bunun yerine ödeme servisinde **sadece NodeBB sunucusunun IP adresinden gelen istekleri kabul eden** bir endpoint bulunur. NodeBB tarafında navigasyon menüsüne eklenen bir link ile kullanıcı bu endpoint'e yönlendirilir.
84
+
85
+ #### Mekanizma
86
+
87
+ - Ödeme servisinde `POST /pay/api/cancel-subscription` endpoint'i oluşturulur
88
+ - Bu endpoint **sadece** izin verilen IP adresinden gelen istekleri kabul eder (NodeBB sunucusunun IP'si)
89
+ - İzin verilen IP adresi admin panelden **Ayarlar** sayfasından değiştirilebilir (veritabanında `Setting` modelinde saklanır)
90
+ - Varsayılan değer `ALLOWED_CANCEL_IP` environment değişkeninden okunur; admin panelden güncelleme yapıldığında veritabanındaki değer önceliklidir
91
+ - NodeBB tarafında navigasyon menüsüne "Aboneliği İptal Et" linki eklenir; bu link kullanıcıyı ödeme servisindeki iptal sayfasına yönlendirir
92
+ - Kullanıcı iptal sayfasında onay vererek aboneliğinin otomatik yenilenmesini kapatır
93
+
94
+ #### İptal Akışı
95
+
96
+ ```
97
+ 1. Kullanıcı NodeBB'de navigasyon menüsünden "Aboneliği İptal Et" linkine tıklar
98
+ → /pay/cancel?uid={uid} sayfası açılır (ödeme servisi)
99
+ 2. Sayfa, kullanıcının aktif abonelik bilgilerini gösterir
100
+ 3. Kullanıcı "Otomatik Yenilemeyi İptal Et" butonuna tıklar
101
+ 4. Frontend, POST /pay/api/cancel-subscription isteği gönderir (uid parametresiyle)
102
+ 5. Endpoint IP kontrolü yapar:
103
+ - İstek NodeBB sunucu IP'sinden gelmiyorsa → 403 Forbidden
104
+ - İstek doğru IP'den geliyorsa → devam et
105
+ 6. Aktif abonelik bulunur:
106
+ - autoRenew = false olarak güncellenir
107
+ - cancelRequestedAt = now kaydedilir
108
+ 7. Kullanıcıya onay sayfası gösterilir:
109
+ "Otomatik yenileme iptal edildi. Aboneliğiniz {bitiş tarihi} tarihine kadar aktif kalacaktır."
110
+ 8. Abonelik mevcut dönem sonuna kadar aktif kalır
111
+ 9. Dönem dolduğunda mevcut cron job (10 dk) normal şekilde plan grubundan çıkarır ve expired işaretler
112
+ ```
113
+
114
+ #### Neden Bu Yaklaşım Güvenli?
115
+
116
+ - Endpoint **sadece** NodeBB sunucusunun IP adresinden gelen istekleri kabul eder
117
+ - Kullanıcı NodeBB'ye giriş yapmadan bu sayfaya erişemez (NodeBB'nin kendi auth sistemi link'i gösterir)
118
+ - Dış dünyadan (farklı IP'lerden) gelen istekler reddedilir
119
+ - Cron job veya geçici grup mekanizmasına gerek yoktur — istek anında işlenir
120
+ - Cross-service auth, HMAC, OTP gibi ek mekanizmalara ihtiyaç yok
121
+
122
+ #### Kullanıcıya Bilgilendirme
123
+
124
+ - İptal sayfasında şu bilgiler gösterilir:
125
+ > "Otomatik yenilemeyi iptal ettiğinizde mevcut abonelik süreniz sonuna kadar erişiminiz devam edecektir. Süre sonunda aboneliğiniz yenilenmeyecektir."
126
+ - İptal işlendikten sonra onay mesajı:
127
+ > "Otomatik yenileme iptal edildi. Aboneliğiniz {bitiş tarihi} tarihine kadar aktif kalacaktır."
128
+
129
+ #### Kullanıcı Fikir Değiştirirse
130
+
131
+ - İptal sadece otomatik yenilemeyi kapatır, mevcut dönem erişimi devam eder
132
+ - Kullanıcı forumdan yeniden `/pay/checkout?uid={uid}` üzerinden aynı planı satın alarak devam edebilir
133
+ - Veya admin panelden otomatik yenileme tekrar açılabilir
134
+
135
+ ### 3.6 NodeBB Grup Yönetimi
136
+
137
+ - Her plan için ayrı NodeBB grubu: `lite`, `premium`, `vip`
138
+ - Kullanıcı **sadece** aktif planının grubunda olur
139
+ - Plan yükseltmede: eski gruptan çıkar, yeni gruba ekle
140
+ - Süre dolduğunda: gruptan çıkar
141
+
142
+ ### 3.7 Admin Paneli — Tam Yetki
143
+
144
+ Admin paneli NodeBB foruma bağlıdır ve admin aşağıdaki tüm işlemleri yapabilir:
145
+
146
+ #### Kullanıcı Doğrulama
147
+
148
+ - Admin, UID veya kullanıcı adı girerek işlem yapar
149
+ - Girilen bilgi NodeBB API üzerinden doğrulanır; kullanıcı bulunamazsa hata gösterilir
150
+ - Doğrulanan kullanıcının profil bilgileri (avatar, username, email, kayıt tarihi) gösterilir
151
+
152
+ #### Manuel Plan Atama
153
+
154
+ - Admin herhangi bir kullanıcıya herhangi bir plan (Lite / Premium / VIP) atayabilir
155
+ - Manuel atama PayTR ödemesi gerektirmez
156
+ - Süre admin tarafından belirlenir (1 ay / 3 ay / 6 ay / 12 ay / özel tarih, varsayılan: 30 gün)
157
+ - Period admin tarafından seçilir (aylık / yıllık)
158
+ - Atama sonrası NodeBB'de ilgili gruba eklenir (eski grup varsa çıkarılır)
159
+ - Subscription kaydı `source: "admin"` ve `adminAssignedBy` ile oluşturulur
160
+
161
+ #### Manuel Plan Değiştirme (Upgrade & Downgrade)
162
+
163
+ - Admin, kullanıcının mevcut planını **herhangi bir plana** değiştirebilir (hem yükseltme hem düşürme)
164
+ - Downgrade dahil — normal kullanıcılar yapamaz ama admin yapabilir
165
+ - Plan değiştirildiğinde: eski NodeBB grubundan çıkarılır, yeni gruba eklenir
166
+ - Bitiş tarihi korunur (admin isterse bitiş tarihini de değiştirebilir)
167
+ - `previousPlan` kaydedilir, `source: "admin"` olarak işaretlenir
168
+
169
+ #### Abonelik İptali
170
+
171
+ - Admin, aktif bir aboneliği anında iptal edebilir
172
+ - İptal sonrası: kullanıcı NodeBB grubundan çıkarılır, status "cancelled" olarak işaretlenir
173
+ - İptal nedeni opsiyonel olarak kaydedilebilir (`cancelReason`)
174
+
175
+ #### Süre Düzenleme
176
+
177
+ - Admin, mevcut aboneliğin bitiş tarihini ileri veya geri alabilir
178
+ - Süre uzatma veya kısaltma serbesttir
179
+
180
+ #### Kullanıcı Detay Sayfası
181
+
182
+ - Tek kullanıcıya ait tüm abonelik geçmişi (aktif + geçmiş) listelenir
183
+ - Mevcut plan, period, başlangıç/bitiş tarihi, kaynak (ödeme/admin/upgrade), otomatik yenileme durumu
184
+ - Üzerinden doğrudan plan değiştirme, iptal, süre düzenleme işlemleri yapılabilir
185
+ - NodeBB'deki grup üyeliği durumu gösterilir
186
+
187
+ ---
188
+
189
+ ## 4. Teknik Tasarım
190
+
191
+ ### 4.1 Veritabanı Modeli Değişiklikleri
192
+
193
+ **Subscription Model** (`src/models/subscription.model.js`) — güncellenecek alanlar:
194
+
195
+ ```javascript
196
+ {
197
+ // Mevcut alanlar (korunacak)
198
+ uid, username, status, startedAt, expiresAt, premiumRevokedAt,
199
+ billingInfo, paytrMerchantOid, paytrRaw,
200
+
201
+ // YENİ ALANLAR
202
+ plan: {
203
+ type: String,
204
+ enum: ["lite", "premium", "vip"],
205
+ required: true,
206
+ index: true
207
+ },
208
+ period: {
209
+ type: String,
210
+ enum: ["monthly", "yearly"],
211
+ default: "monthly"
212
+ },
213
+ amount: {
214
+ type: Number, // kuruş cinsinden ödenen tutar
215
+ required: true
216
+ },
217
+ source: {
218
+ type: String,
219
+ enum: ["payment", "admin", "upgrade"],
220
+ default: "payment"
221
+ },
222
+ previousPlan: {
223
+ type: String,
224
+ enum: ["lite", "premium", "vip", null],
225
+ default: null
226
+ },
227
+ autoRenew: {
228
+ type: Boolean,
229
+ default: true // varsayılan olarak açık
230
+ },
231
+ adminAssignedBy: {
232
+ type: String, // admin username (null if not admin-assigned)
233
+ default: null
234
+ },
235
+ cancelReason: {
236
+ type: String, // admin tarafından iptal edildiğinde sebep (opsiyonel)
237
+ default: null
238
+ }
239
+ }
240
+ ```
241
+
242
+ **Yeni Model: Setting** (`src/models/setting.model.js`) — admin panelden yönetilebilen ayarlar:
243
+
244
+ ```javascript
245
+ {
246
+ key: {
247
+ type: String,
248
+ required: true,
249
+ unique: true,
250
+ index: true
251
+ },
252
+ value: {
253
+ type: String,
254
+ required: true
255
+ },
256
+ updatedBy: {
257
+ type: String, // admin username
258
+ default: null
259
+ },
260
+ updatedAt: {
261
+ type: Date,
262
+ default: Date.now
263
+ }
264
+ }
265
+ ```
266
+
267
+ Önceden tanımlı ayar anahtarları:
268
+ - `allowedCancelIp` — Abonelik iptali endpoint'inin kabul ettiği IP adresi. Boş veya kayıt yoksa `ALLOWED_CANCEL_IP` env değişkenine fallback yapılır.
269
+
270
+ **Yeni Model: Plan Config** (`src/models/plan.model.js`) — plan tanımları için (opsiyonel, config dosyasında da tutulabilir):
271
+
272
+ Plan tanımları `src/config/plans.js` config dosyasında tutulacak (veritabanında değil):
273
+
274
+ ```javascript
275
+ const PLANS = {
276
+ lite: {
277
+ name: "Lite",
278
+ slug: "lite",
279
+ level: 1,
280
+ monthlyPrice: 11900, // kuruş
281
+ yearlyPrice: 47600, // kuruş (8 ay bedava)
282
+ nodebbGroup: "lite",
283
+ features: ["Tüm çalışma sorularına sınırsız erişim", "En güncel ders notları arşivi"],
284
+ },
285
+ premium: {
286
+ name: "Premium",
287
+ slug: "premium",
288
+ level: 2,
289
+ monthlyPrice: 13900,
290
+ yearlyPrice: 55600,
291
+ nodebbGroup: "premium",
292
+ badge: "En Popüler",
293
+ features: ["Lite planındaki her şey", "Gelişmiş PDF önizleme ve araçları", "Akıllı CV Oluşturucu", "Hedef odaklı GPA Hesaplayıcı", "Sıfır Reklam", "Niki Wallet: %30 indirimli kahve hediye", "Sosyal Analitik"],
294
+ },
295
+ vip: {
296
+ name: "VIP",
297
+ slug: "vip",
298
+ level: 3,
299
+ monthlyPrice: 15900,
300
+ yearlyPrice: 63600,
301
+ nodebbGroup: "vip",
302
+ badge: "Elit Deneyim",
303
+ features: ["Premium'daki her şey", "Prestij: Profilde özel VIP Badge", "Öncülük: Yeni özelliklere erken erişim"],
304
+ },
305
+ };
306
+ ```
307
+
308
+ ### 4.2 Değiştirilecek Dosyalar
309
+
310
+ | Dosya | Değişiklik |
311
+ | --------------------------------------- | ------------------------------------------------------------------------------- |
312
+ | `src/models/subscription.model.js` | plan, period, amount, source, previousPlan, adminAssignedBy alanları eklenir |
313
+ | `src/config/plans.js` | **YENİ** — Plan tanımları ve fiyatlandırma |
314
+ | `src/config/env.js` | Yeni NodeBB grup env değişkenleri (veya plans.js'den okunur) |
315
+ | `src/services/paytr.service.js` | Dinamik fiyat ve ürün adı desteği (plan + period parametreleri) |
316
+ | `src/services/nodebb.service.js` | Çoklu grup desteği: addToGroup(uid, groupName), removeFromGroup(uid, groupName) |
317
+ | `src/services/subscription.service.js` | createOrExtend → plan-aware, upgrade hesaplaması, admin atama |
318
+ | `src/services/admin.service.js` | Plan bazlı istatistikler, manuel atama servisi, gelir hesabı güncelleme |
319
+ | `src/controllers/payment.controller.js` | Plan seçimi ve period akışı, upgrade flow |
320
+ | `src/controllers/admin.controller.js` | Manuel plan atama endpoint'i, gelişmiş dashboard |
321
+ | `src/routes/payment.routes.js` | Yeni route'lar (plan seçim, upgrade) |
322
+ | `src/routes/admin.routes.js` | Manuel atama route'ları |
323
+ | `src/jobs/subscriptionWatcher.job.js` | Plan-aware grup çıkarma |
324
+ | `src/views/home.ejs` | 3 kart plan seçim UI, aylık/yıllık toggle |
325
+ | `src/views/billing-form.ejs` | Plan ve period bilgisi taşıma |
326
+ | `src/views/payment.ejs` | Seçilen plan bilgisi gösterme |
327
+ | `src/views/result.ejs` | Plan adı gösterme |
328
+ | `src/models/setting.model.js` | **YENİ** — Ayarlar modeli (key-value, admin panelden yönetilen yapılandırmalar) |
329
+ | `src/middleware/ipRestrict.js` | **YENİ** — IP kısıtlama middleware'i (DB'den veya env'den IP okur) |
330
+ | `src/views/cancel.ejs` | **YENİ** — Kullanıcı iptal sayfası (abonelik bilgileri + iptal butonu) |
331
+ | `src/views/admin/dashboard.ejs` | Plan bazlı istatistikler, gelişmiş grafikler |
332
+ | `src/views/admin/subscribers.ejs` | Plan filtresi, manuel atama butonu |
333
+ | `src/views/admin/assign.ejs` | **YENİ** — Admin kullanıcı arama + manuel plan atama formu |
334
+ | `src/views/admin/subscriber-detail.ejs` | **YENİ** — Tek kullanıcı detay sayfası (plan değiştir, iptal, süre düzenle) |
335
+ | `src/views/admin/settings.ejs` | **YENİ** — Ayarlar sayfası (IP adresi vb. yapılandırmalar) |
336
+ | `.env.example` | Yeni env değişkenleri |
337
+
338
+ ### 4.3 API Endpoint'leri
339
+
340
+ #### Mevcut Route'lar (Güncelleme)
341
+
342
+ - `GET /pay/checkout?uid={uid}` → Plan seçim sayfası (3 kart + aylık/yıllık toggle)
343
+ - `GET /pay/checkout/billing?uid={uid}&plan={slug}&period={monthly|yearly}` → Fatura formu
344
+ - `POST /pay/checkout/billing` → Form işleme (plan + period eklenir)
345
+ - `GET /pay/checkout/start?uid={uid}&plan={slug}&period={monthly|yearly}&...` → PayTR başlat
346
+ - `POST /pay/checkout/callback` → PayTR callback (plan bilgisi merchantOid'den parse)
347
+ - `GET /pay/checkout/result` → Sonuç sayfası
348
+
349
+ #### Yeni Route'lar
350
+
351
+ - `GET /pay/checkout/upgrade?uid={uid}&plan={slug}` → Upgrade sayfası (fark ücreti göster)
352
+ - `POST /pay/checkout/upgrade` → Upgrade işlemi başlat → PayTR'ye yönlendir
353
+ - `GET /pay/cancel?uid={uid}` → İptal sayfası (abonelik bilgileri + iptal butonu)
354
+ - `POST /pay/api/cancel-subscription` → IP kısıtlamalı iptal endpoint'i (sadece NodeBB sunucu IP'sinden)
355
+
356
+ #### Admin Route'ları (Güncelleme + Yeni)
357
+
358
+ - `GET /pay/admin` → Gelişmiş dashboard (plan bazlı breakdown)
359
+ - `GET /pay/admin/subscribers` → Plan filtresiyle liste
360
+ - `GET /pay/admin/subscriber/:uid` → **YENİ** Tek kullanıcı detay sayfası (abonelik geçmişi + işlemler)
361
+ - `GET /pay/admin/assign` → **YENİ** Manuel plan atama formu (UID/username ile kullanıcı arama)
362
+ - `POST /pay/admin/assign` → **YENİ** Manuel plan atama işlemi
363
+ - `POST /pay/admin/change-plan` → **YENİ** Plan değiştirme (upgrade/downgrade)
364
+ - `POST /pay/admin/cancel` → **YENİ** Abonelik iptal etme
365
+ - `POST /pay/admin/extend` → **YENİ** Süre düzenleme (uzatma/kısaltma)
366
+ - `GET /pay/admin/lookup?q={uid|username}` → **YENİ** NodeBB kullanıcı arama (AJAX)
367
+ - `GET /pay/admin/settings` → **YENİ** Ayarlar sayfası (IP adresi vb. yapılandırmalar)
368
+ - `POST /pay/admin/settings` → **YENİ** Ayarları kaydet
369
+
370
+ ### 4.4 Ödeme Akışı (Güncel)
371
+
372
+ ```
373
+ 1. Kullanıcı → /pay/checkout?uid=123
374
+ 2. Plan seçim sayfası gösterilir (3 kart + aylık/yıllık toggle)
375
+ - Aktif aboneliği varsa: mevcut plan gösterilir + sadece üst planlar seçilebilir (upgrade)
376
+ - Aktif aboneliği yoksa: tüm planlar seçilebilir
377
+ 3. Kullanıcı plan + period seçer → "Satın Al" butonuna tıklar
378
+ 4. Billing form → Plan ve period bilgisi hidden field'larda taşınır
379
+ 5. PayTR'ye gönderilir:
380
+ - merchantOid formatı: {uid}x{planSlug}x{period}x{timestamp}
381
+ - Tutar: plans.js'den ilgili fiyat
382
+ - Basket: Plan adı + period
383
+ 6. PayTR callback gelir → merchantOid parse edilir → plan + period çıkarılır
384
+ 7. NodeBB'de ilgili gruba eklenir (eski grup varsa önce çıkarılır)
385
+ 8. Subscription kaydı oluşturulur (plan, period, amount bilgileriyle)
386
+ ```
387
+
388
+ ### 4.5 Upgrade Akışı
389
+
390
+ ```
391
+ 1. Kullanıcı → /pay/checkout?uid=123 (aktif aboneliği var)
392
+ 2. Mevcut planı gösterilir, üst planların "Yükselt" butonu aktif
393
+ 3. "Yükselt" → /pay/checkout/upgrade?uid=123&plan=premium
394
+ 4. Prorated fark ücreti hesaplanıp gösterilir:
395
+ - kalanGün = (expiresAt - now) / (1000*60*60*24)
396
+ - eskiGünlükFiyat = eskiPlan.fiyat / toplamGün
397
+ - yeniGünlükFiyat = yeniPlan.fiyat / toplamGün
398
+ - fark = (yeniGünlükFiyat - eskiGünlükFiyat) * kalanGün
399
+ - Minimum fark: 1₺ (100 kuruş)
400
+ 5. Kullanıcı onaylar → Billing form → PayTR (fark ücreti kadar)
401
+ 6. Callback → Eski gruptan çıkar, yeni gruba ekle
402
+ 7. Subscription güncelle: plan değişir, expiresAt aynı kalır, previousPlan kaydedilir
403
+ ```
404
+
405
+ ### 4.6 Admin Paneli Güncellemeleri
406
+
407
+ #### Dashboard İstatistikleri
408
+
409
+ - **Toplam Gelir**: Plan ve period bazlı breakdown
410
+ - **Aktif Aboneler**: Plan bazlı dağılım (pasta grafik veya bar)
411
+ - **Plan Dağılımı**: Lite vs Premium vs VIP oranları
412
+ - **Aylık vs Yıllık**: Period dağılımı
413
+ - **Bu Ay Gelir**: Plan bazlı
414
+ - **Son Abonelikler**: Plan bilgisiyle birlikte
415
+ - **Aylık Gelir Grafiği**: Plan bazlı stacked bar chart
416
+
417
+ #### Kullanıcı Arama & Atama Sayfası (`assign.ejs`)
418
+
419
+ - UID veya kullanıcı adı giriş alanı
420
+ - "Ara" butonuna basıldığında NodeBB API'den kullanıcı doğrulanır (AJAX ile `/pay/admin/lookup`)
421
+ - Kullanıcı bulunamazsa hata mesajı gösterilir
422
+ - Bulunursa: avatar, username, email, kayıt tarihi ve mevcut abonelik durumu gösterilir
423
+ - Plan seçimi (Lite / Premium / VIP)
424
+ - Period seçimi (Aylık / Yıllık)
425
+ - Süre seçimi (1 ay / 3 ay / 6 ay / 12 ay / özel tarih)
426
+ - "Ata" butonuna basıldığında:
427
+ - Subscription kaydı oluşturulur (source: "admin", adminAssignedBy kaydedilir)
428
+ - NodeBB grubuna eklenir (eski grup varsa çıkarılır)
429
+
430
+ #### Ayarlar Sayfası (`settings.ejs`) — YENİ
431
+
432
+ - Admin panelde **Ayarlar** menü öğesi eklenir
433
+ - Ayarlar sayfasında sistem genelindeki yapılandırmalar yönetilir:
434
+ - **İptal IP Adresi**: Abonelik iptali endpoint'inin kabul ettiği IP adresi (NodeBB sunucu IP'si)
435
+ - Mevcut IP gösterilir
436
+ - Yeni IP girilerek güncellenebilir
437
+ - Boş bırakılırsa `ALLOWED_CANCEL_IP` env değişkenine fallback yapılır
438
+ - Ayarlar veritabanında `Setting` modelinde key-value olarak saklanır
439
+
440
+ #### Kullanıcı Detay Sayfası (`subscriber-detail.ejs`)
441
+
442
+ - NodeBB'den çekilen profil bilgileri (avatar, username, email, uid)
443
+ - Mevcut aktif abonelik bilgisi (plan, period, başlangıç/bitiş, kaynak, otomatik yenileme)
444
+ - NodeBB grup üyeliği durumu
445
+ - **İşlem butonları:**
446
+ - **Plan Değiştir**: Dropdown ile yeni plan seç → onayda upgrade veya downgrade yapılır
447
+ - **Süre Düzenle**: Bitiş tarihini değiştir (date picker)
448
+ - **Aboneliği İptal Et**: Onay modal → iptal nedeni (opsiyonel) → iptal işlemi
449
+ - Geçmiş abonelik kayıtları tablosu (tüm subscription history)
450
+
451
+ #### Abone Listesi (`subscribers.ejs`)
452
+
453
+ - Mevcut filtreler + plan filtresi (Lite / Premium / VIP)
454
+ - Period filtresi (Aylık / Yıllık)
455
+ - Source filtresi (Ödeme / Admin / Upgrade)
456
+ - Her satırda plan badge'i gösterilir
457
+ - Her satırda kullanıcı detay sayfasına link
458
+ - Hızlı işlem butonları (iptal, plan değiştir)
459
+
460
+ ---
461
+
462
+ ## 5. NodeBB Entegrasyonu (Plugin Yok)
463
+
464
+ NodeBB tarafında **herhangi bir plugin yazılmaz**. Tüm iletişim NodeBB'nin yerleşik grup sistemi ve API'si üzerinden sağlanır.
465
+
466
+ ### 5.1 Güvenlik Modeli
467
+
468
+ | İşlem | Güvenlik Riski | Çözüm |
469
+ | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ | ---------------------------------------------- |
470
+ | **Plan satın alma** (`/pay/checkout?uid={uid}`) | Düşük — saldırgan başkasının uid'sini girse bile kendi parasıyla başkasına abonelik alır (hediye gibi) | Mevcut akış yeterli |
471
+ | **Plan yükseltme** (`/pay/checkout/upgrade?uid={uid}`) | Düşük — aynı mantık, saldırgan kendi kartıyla ödeme yapar | Mevcut akış yeterli |
472
+ | **Abonelik iptali** | Yüksek — başkasının aboneliğini iptal etme riski | IP kısıtlamalı endpoint ile çözüldü (bkz. 3.5) |
473
+
474
+ Sonuç: Ödeme gerektiren işlemlerde cross-service auth gerekmez (PayTR güvenliği yeterli). İptal endpoint'i sadece NodeBB sunucusunun IP'sinden gelen istekleri kabul eder.
475
+
476
+ ### 5.2 NodeBB Tarafında Yapılacaklar (Admin Panelden, Kod Yazmadan)
477
+
478
+ #### 1. Grupları Oluştur
479
+
480
+ NodeBB Admin Panel → Groups → Create:
481
+
482
+ | Grup Adı | Tür | Amaç |
483
+ | --------- | -------------- | -------------------- |
484
+ | `lite` | Gizli (Hidden) | Lite plan üyeleri |
485
+ | `premium` | Gizli (Hidden) | Premium plan üyeleri |
486
+ | `vip` | Gizli (Hidden) | VIP plan üyeleri |
487
+
488
+ - Plan grupları (`lite`, `premium`, `vip`) → **Hidden** ve sadece API ile yönetilir. Kullanıcılar kendileri katılamaz/çıkamaz.
489
+
490
+ #### 2. Navigasyon Linkleri
491
+
492
+ NodeBB Admin Panel → Navigation:
493
+
494
+ - **"Planını Yükselt"** → `https://pay.ieu.app/pay/checkout?uid={uid}` (NodeBB template değişkeni ile uid otomatik eklenir)
495
+ - **"Aboneliği İptal Et"** → `https://pay.ieu.app/pay/cancel?uid={uid}` (ödeme servisindeki iptal sayfasına yönlendirir)
496
+
497
+ ### 5.3 Ödeme Servisi Tarafında — IP Kısıtlamalı İptal Endpoint'i
498
+
499
+ Ödeme servisinde kullanıcının abonelik iptalini işleyen, IP kısıtlamalı endpoint:
500
+
501
+ #### Endpoint: `POST /pay/api/cancel-subscription`
502
+
503
+ ```
504
+ Request:
505
+ Body: { uid: Number }
506
+ IP Kontrolü: Sadece ALLOWED_CANCEL_IP env değişkeninde tanımlı IP'den gelen istekler kabul edilir
507
+
508
+ İşlem:
509
+ 1. IP kontrolü yap:
510
+ - req.ip veya x-forwarded-for header'ından gelen IP kontrol edilir
511
+ - ALLOWED_CANCEL_IP ile eşleşmiyorsa → 403 Forbidden döndür
512
+ 2. MongoDB'de aktif abonelik ara (uid, status: "active")
513
+ 3. Aktif abonelik varsa:
514
+ - autoRenew = false
515
+ - cancelRequestedAt = new Date()
516
+ 4. Aktif abonelik yoksa → 404 döndür
517
+ 5. Başarılı yanıt döndür (abonelik bitiş tarihi bilgisiyle)
518
+
519
+ Response (200):
520
+ { success: true, expiresAt: "2025-03-15T00:00:00Z", message: "Otomatik yenileme iptal edildi." }
521
+
522
+ Response (403):
523
+ { error: "Forbidden" }
524
+
525
+ Response (404):
526
+ { error: "Aktif abonelik bulunamadı." }
527
+ ```
528
+
529
+ #### İptal Sayfası: `GET /pay/cancel?uid={uid}`
530
+
531
+ - Kullanıcının aktif abonelik bilgilerini gösterir (plan, bitiş tarihi, otomatik yenileme durumu)
532
+ - "Otomatik Yenilemeyi İptal Et" butonu
533
+ - Butona tıklandığında `POST /pay/api/cancel-subscription` çağrılır
534
+ - İşlem sonrası onay mesajı gösterilir
535
+
536
+ #### IP Kısıtlama Middleware'i
537
+
538
+ ```javascript
539
+ // src/middleware/ipRestrict.js
540
+ const Setting = require("../models/setting.model");
541
+
542
+ async function ipRestrict(req, res, next) {
543
+ // Önce veritabanından admin panelde tanımlanan IP'yi kontrol et
544
+ const setting = await Setting.findOne({ key: "allowedCancelIp" });
545
+ const allowedIp = setting?.value || process.env.ALLOWED_CANCEL_IP;
546
+
547
+ const clientIp = req.ip || req.headers["x-forwarded-for"]?.split(",")[0].trim();
548
+ if (clientIp !== allowedIp) {
549
+ return res.status(403).json({ error: "Forbidden" });
550
+ }
551
+ next();
552
+ }
553
+ ```
554
+
555
+ > **Not**: IP adresi öncelik sırası: 1) Veritabanındaki `allowedCancelIp` ayarı (admin panelden yönetilir), 2) `ALLOWED_CANCEL_IP` environment değişkeni. Admin panelden ayar yapıldığında sunucu yeniden başlatılmadan IP değişikliği uygulanır.
556
+
557
+ ### 5.4 Akış Diyagramları
558
+
559
+ #### Kullanıcı Aboneliğini İptal Eder
560
+
561
+ ```
562
+ Kullanıcı (forum.ieu.app) Ödeme Servisi (pay.ieu.app)
563
+ │ │
564
+ │ "Aboneliği İptal Et" linkine tıklar │
565
+ │ → /pay/cancel?uid=123 │
566
+ │─────────────────────────────────────────────────────────────►│
567
+ │ │
568
+ │ İptal sayfası (aktif abonelik bilgileri + iptal butonu) │
569
+ │◄─────────────────────────────────────────────────────────────│
570
+ │ │
571
+ │ "Otomatik Yenilemeyi İptal Et" butonuna tıklar │
572
+ │ POST /pay/api/cancel-subscription {uid: 123} │
573
+ │─────────────────────────────────────────────────────────────►│
574
+ │ │
575
+ │ IP kontrolü (ALLOWED_CANCEL_IP)
576
+ │ ✓ Geçerli IP → devam et
577
+ │ │
578
+ │ autoRenew = false
579
+ │ cancelRequestedAt = now
580
+ │ │
581
+ │ Onay: "İptal edildi, aboneliğiniz │
582
+ │ {tarih}'e kadar aktif kalacaktır" │
583
+ │◄─────────────────────────────────────────────────────────────│
584
+ ```
585
+
586
+ #### Kullanıcı Plan Satın Alır
587
+
588
+ ```
589
+ Kullanıcı (forum.ieu.app) Ödeme Servisi (pay.ieu.app)
590
+ │ │
591
+ │ "Planını Yükselt" tıklar │
592
+ │ → /pay/checkout?uid=123 │
593
+ │─────────────────────────────────────────────────────────────►│
594
+ │ │
595
+ │ Plan seçim sayfası (3 kart + aylık/yıllık) │
596
+ │◄─────────────────────────────────────────────────────────────│
597
+ │ │
598
+ │ Plan seçer → billing form → PayTR ödeme │
599
+ │─────────────────────────────────────────────────────────────►│
600
+ │ │
601
+ │ PayTR callback → NodeBB grubuna ekle
602
+ │ → Subscription oluştur │
603
+ │ │
604
+ │ result?status=success │
605
+ │◄─────────────────────────────────────────────────────────────│
606
+ ```
607
+
608
+ ---
609
+
610
+ ## 6. Frontend Tasarım
611
+
612
+ ### 6.1 Plan Seçim Sayfası (home.ejs)
613
+
614
+ - 3 kart yan yana (mobilde alt alta)
615
+ - Aylık/Yıllık toggle switch (üstte)
616
+ - Yıllık seçildiğinde: "8 ay bedava!" badge, aylık eşdeğer fiyat gösterimi
617
+ - Premium kartı: "En Popüler" badge, hafif vurgu (border/shadow)
618
+ - VIP kartı: "Elit Deneyim" badge
619
+ - Her kartta: Özellik listesi + fiyat + "Satın Al" butonu
620
+ - Aktif aboneliği olan kullanıcı: mevcut plan vurgulanır, alt planlar disabled, üst planlarda "Yükselt" butonu
621
+
622
+ ### 6.2 Upgrade Sayfası
623
+
624
+ - Mevcut plan vs yeni plan karşılaştırması
625
+ - Prorated fark ücreti hesabı gösterimi
626
+ - Kalan gün bilgisi
627
+ - Onay butonu → billing form'a yönlendir
628
+
629
+ ---
630
+
631
+ ## 7. Yasal Uyumluluk ve Bilgilendirmeler
632
+
633
+ Ödeme içeren bir abonelik sistemi olduğundan, Türkiye mevzuatına tam uyum sağlanmalıdır.
634
+
635
+ ### 7.1 Uyulması Gereken Mevzuat
636
+
637
+ | Mevzuat | Kapsam |
638
+ | ---------------------------------------------------------------- | ------------------------------------------------------------------- |
639
+ | **6502 sayılı Tüketicinin Korunması Hakkında Kanun** | Tüketici hakları, cayma hakkı, haksız şartlar |
640
+ | **Mesafeli Sözleşmeler Yönetmeliği** | Ön bilgilendirme, sözleşme, cayma hakkı, dijital içerik istisnaları |
641
+ | **6698 sayılı KVKK** | Kişisel verilerin korunması, aydınlatma yükümlülüğü, açık rıza |
642
+ | **6563 sayılı Elektronik Ticaretin Düzenlenmesi Hakkında Kanun** | Hizmet sağlayıcı bilgileri, ticari iletişim, sipariş onayı |
643
+ | **5651 sayılı İnternet Kanunu** | İçerik sağlayıcı sorumlulukları |
644
+ | **Abonelik Sözleşmeleri Yönetmeliği** | Otomatik yenileme bilgilendirme, abonelik iptali |
645
+
646
+ ### 7.2 Satın Alma Öncesi Bilgilendirmeler
647
+
648
+ Kullanıcı ödeme yapmadan önce aşağıdaki bilgilendirmeler **açıkça** sunulmalı ve onay alınmalıdır:
649
+
650
+ #### Ön Bilgilendirme Formu (Zorunlu)
651
+
652
+ Plan seçim sayfasında veya billing formunda kullanıcıya gösterilecek:
653
+
654
+ - Hizmet sağlayıcı bilgileri (unvan, adres, iletişim)
655
+ - Seçilen planın adı, kapsamı ve özellikleri
656
+ - Toplam fiyat (KDV dahil, kuruş cinsinden net tutar)
657
+ - Ödeme yöntemi ve koşulları
658
+ - Abonelik süresi (aylık: 30 gün, yıllık: 365 gün)
659
+ - Otomatik yenileme durumu ve koşulları
660
+ - Cayma hakkı bilgilendirmesi (dijital içerik istisnası dahil)
661
+ - Şikayet ve itiraz mekanizmaları
662
+
663
+ #### Onay Checkbox'ları (Satın alma butonundan önce)
664
+
665
+ Kullanıcının tüm checkbox'ları işaretlemesi zorunludur:
666
+
667
+ 1. **Sözleşme onayı** (mevcut, güncellenmeli):
668
+
669
+ > "Mesafeli Satış Sözleşmesi'ni, Kullanım Koşulları'nı, Gizlilik Politikası'nı ve KVKK Aydınlatma Metni'ni okudum, anladım ve kabul ediyorum."
670
+
671
+ 2. **Dijital içerik cayma hakkı feragati** (mevcut, güncellenmeli):
672
+
673
+ > "Dijital içerik hizmetinin derhal ifasına başlanmasını talep ettiğimi ve bu sebeple 14 günlük cayma hakkımdan feragat ettiğimi onaylıyorum."
674
+
675
+ 3. **Otomatik yenileme bilgilendirmesi** (YENİ — varsayılan tikli):
676
+
677
+ > "Abonelik sürem dolduğunda, aynı plan ve ödeme tutarı ile otomatik olarak yenileneceğini biliyorum. Otomatik yenilemeyi istediğim zaman iptal edebileceğimi anlıyorum."
678
+
679
+ 4. **Ön bilgilendirme formu onayı** (YENİ):
680
+ > "Ön Bilgilendirme Formu'nu okudum ve satın alma öncesi gerekli tüm bilgileri aldım."
681
+
682
+ ### 7.3 Otomatik Yenileme — Yasal Gereklilikler
683
+
684
+ Abonelik Sözleşmeleri Yönetmeliği gereği:
685
+
686
+ - **Satış anında**: Otomatik yenileme tercihi açıkça gösterilmeli, fiyat ve yenilenme tarihi belirtilmeli
687
+ - **Yenileme öncesi bildirim**: Abonelik sona ermeden **en az 3 gün önce** kullanıcıya e-posta ile bildirim gönderilmeli:
688
+ - Yenilenecek plan adı
689
+ - Yenileme tutarı
690
+ - Yenileme tarihi
691
+ - İptal etme linki / talimatları
692
+ - **Kolay iptal**: Kullanıcı otomatik yenilemeyi kolayca kapatabilmeli (forum profili veya ödeme sayfası üzerinden)
693
+ - **İptal onayı**: Otomatik yenileme kapatıldığında kullanıcıya onay bildirimi gönderilmeli
694
+
695
+ ### 7.4 Cayma Hakkı ve İade Politikası
696
+
697
+ 6502 sayılı Kanun ve Mesafeli Sözleşmeler Yönetmeliği kapsamında:
698
+
699
+ - **Genel kural**: Mesafeli satışlarda tüketici 14 gün içinde cayma hakkına sahiptir
700
+ - **Dijital içerik istisnası**: Elektronik ortamda anında ifa edilen dijital içeriklerde, tüketicinin önceden açık onayı ve cayma hakkından feragat beyanı alınmışsa cayma hakkı uygulanmaz
701
+ - **Bu sistemde**: Kullanıcı satın alma öncesinde dijital içerik feragat checkbox'ını onayladığı için cayma hakkı bulunmaz
702
+ - **İade**: İade politikası sayfasında (`legal-refund.ejs`) bu durum açıkça belirtilmelidir
703
+ - **Admin iptali**: Admin tarafından yapılan iptallerde iade kararı admin'in insiyatifindedir
704
+
705
+ ### 7.5 Güncellenecek Yasal Sayfalar
706
+
707
+ Mevcut yasal sayfalar çok katmanlı plan yapısına göre güncellenmelidir:
708
+
709
+ | Sayfa | Güncellenecek İçerik |
710
+ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
711
+ | `legal-distance.ejs` | Plan isimleri (Lite/Premium/VIP), fiyat tablosu, otomatik yenileme maddeleri, yıllık abonelik koşulları, upgrade/downgrade kuralları |
712
+ | `legal-terms.ejs` | Çoklu plan tanımları, plan yükseltme/düşürme koşulları, otomatik yenileme kuralları, admin iptal yetkisi |
713
+ | `legal-refund.ejs` | Plan bazlı iade kuralları, upgrade'de prorated hesaplama açıklaması, otomatik yenileme iptali prosedürü |
714
+ | `legal-privacy.ejs` | Otomatik yenileme için saklanan ödeme bilgileri, plan tercihi verisi |
715
+ | `legal-kvkk.ejs` | Ödeme bilgileri işleme amacı, otomatik yenileme kapsamında veri saklama süresi |
716
+
717
+ ### 7.6 Satın Alma Sonrası Bilgilendirmeler
718
+
719
+ #### Sipariş Onay E-postası / Ekranı
720
+
721
+ Ödeme başarılı olduğunda kullanıcıya gösterilecek ve (e-posta altyapısı varsa) gönderilecek bilgiler:
722
+
723
+ - Satın alınan plan adı ve özellikleri
724
+ - Ödenen tutar (KDV dahil)
725
+ - Abonelik başlangıç ve bitiş tarihi
726
+ - Otomatik yenileme durumu (açık/kapalı)
727
+ - Bir sonraki yenileme tarihi ve tutarı (otomatik yenileme açıksa)
728
+ - İptal ve iletişim bilgileri
729
+
730
+ #### Result Sayfası (`result.ejs`)
731
+
732
+ Başarılı ödeme sonrası gösterilecek ek bilgiler:
733
+
734
+ - Seçilen plan adı
735
+ - Abonelik bitiş tarihi
736
+ - Otomatik yenileme durumu
737
+ - Destek iletişim bilgileri
738
+
739
+ ### 7.7 Fiyat Gösterimi Kuralları
740
+
741
+ - Tüm fiyatlar **KDV dahil** olarak gösterilmelidir
742
+ - Para birimi açıkça belirtilmelidir (₺ / TL)
743
+ - Yıllık planda: toplam tutar + aylık eşdeğer fiyat + tasarruf miktarı gösterilmelidir
744
+ - Upgrade'de: fark ücreti hesaplaması kullanıcıya şeffaf şekilde gösterilmelidir (kalan gün, günlük fark, toplam fark)
745
+ - İndirimli fiyat gösteriminde eski fiyat ve indirim oranı belirtilmelidir
746
+
747
+ ### 7.8 Hizmet Sağlayıcı Bilgileri
748
+
749
+ Tüm sayfalarda (footer veya erişilebilir bir yerde) bulunması gereken bilgiler:
750
+
751
+ - Hizmet sağlayıcı unvanı
752
+ - İletişim e-posta adresi
753
+ - Website adresi
754
+ - Şikayet/destek kanalı
755
+
756
+ ---
757
+
758
+ ## 8. Environment Değişkenleri
759
+
760
+ ```env
761
+ # Mevcut değişkenler korunur, ek olarak:
762
+ NODEBB_LITE_GROUP=lite
763
+ NODEBB_PREMIUM_GROUP=premium
764
+ NODEBB_VIP_GROUP=vip
765
+ ALLOWED_CANCEL_IP=1.2.3.4 # NodeBB sunucusunun IP adresi — varsayılan değer; admin panelden güncellenebilir (DB'deki değer önceliklidir)
766
+ ```
767
+
768
+ > Not: Grup isimleri `src/config/plans.js` içinde tanımlı olacağından, env'den de okunabilir veya direkt config'te sabitlenebilir.
769
+
770
+ ---
771
+
772
+ ## 9. Migration / Geçiş Planı
773
+
774
+ Mevcut "premium" grubundaki kullanıcılar için geçiş:
775
+
776
+ 1. Mevcut tüm aktif aboneliklere `plan: "premium"` ve `period: "monthly"` atanır (geriye dönük uyumluluk)
777
+ 2. `amount: 9990` (mevcut fiyat 99.90₺) olarak set edilir
778
+ 3. NodeBB'de mevcut "premium" grubundaki kullanıcılar "premium" grubunda kalır (grup adı aynı)
779
+ 4. Migration script'i `src/scripts/migrate-plans.js` olarak yazılır
780
+
781
+ ---
782
+
783
+ ## 10. Uygulama Sırası
784
+
785
+ ### Faz 1: Altyapı
786
+
787
+ 1. `src/config/plans.js` oluştur — plan tanımları
788
+ 2. `subscription.model.js` güncelle — yeni alanlar
789
+ 3. Migration script yaz ve çalıştır
790
+
791
+ ### Faz 2: Servis Katmanı
792
+
793
+ 4. `nodebb.service.js` güncelle — çoklu grup desteği
794
+ 5. `paytr.service.js` güncelle — dinamik fiyat/ürün
795
+ 6. `subscription.service.js` güncelle — plan-aware CRUD + upgrade hesaplama
796
+
797
+ ### Faz 3: Ödeme Akışı
798
+
799
+ 7. `payment.controller.js` güncelle — plan seçimi + upgrade flow
800
+ 8. `payment.routes.js` güncelle — yeni route'lar
801
+ 9. Frontend: `home.ejs` (plan kartları), `billing-form.ejs`, `payment.ejs`, `result.ejs`
802
+
803
+ ### Faz 4: Admin Paneli
804
+
805
+ 10. `admin.service.js` güncelle — plan bazlı istatistikler + manuel atama
806
+ 11. `admin.controller.js` güncelle — yeni endpoint'ler
807
+ 12. `admin.routes.js` güncelle — yeni route'lar
808
+ 13. Frontend: `dashboard.ejs`, `subscribers.ejs`, `assign.ejs` (yeni)
809
+
810
+ ### Faz 5: Yasal Uyumluluk
811
+
812
+ 14. Yasal sayfalar güncelle — çoklu plan, otomatik yenileme, upgrade/downgrade kuralları
813
+ 15. Ön bilgilendirme formu ve onay checkbox'ları ekle
814
+ 16. Otomatik yenileme öncesi bildirim mekanizması (cron + e-posta)
815
+
816
+ ### Faz 6: Test & Doğrulama
817
+
818
+ 17. `.env.example` güncelle
819
+ 18. Tüm akışları test et (yeni abone, upgrade, admin atama, süre dolumu, yasal onaylar)
820
+
821
+ ---
822
+
823
+ ## 11. Doğrulama (Verification)
824
+
825
+ ### Test Senaryoları
826
+
827
+ 1. **Yeni Abonelik**: Her 3 plan x 2 period = 6 senaryo test
828
+ 2. **Upgrade**: Lite→Premium, Lite→VIP, Premium→VIP = 3 senaryo
829
+ 3. **Admin Atama**: Her plan için manuel atama testi
830
+ 4. **Süre Dolumu**: Cron job'un doğru grubu kaldırdığını doğrula
831
+ 5. **NodeBB Grup**: Upgrade'de eski gruptan çıkıp yeni gruba girdiğini doğrula
832
+ 6. **PayTR Callback**: merchantOid parse'ın doğru çalıştığını doğrula
833
+ 7. **Prorated Hesaplama**: Fark ücretinin doğru hesaplandığını doğrula
834
+ 8. **Dashboard**: Plan bazlı istatistiklerin doğru gösterildiğini doğrula
835
+ 9. **Yasal Onaylar**: Checkbox'lar işaretlenmeden ödeme butonunun aktif olmadığını doğrula
836
+ 10. **Otomatik Yenileme Bildirimi**: Süre dolmadan 3 gün önce bildirim cron job'ının çalıştığını doğrula
837
+ 11. **Yasal Sayfalar**: Tüm yasal sayfalarda plan isimleri, fiyatlar ve otomatik yenileme bilgilerinin güncel olduğunu doğrula
838
+
839
+ ### Test Yöntemi
840
+
841
+ - PayTR test mode ile ödeme akışı testi
842
+ - MongoDB'de kayıt doğrulama
843
+ - NodeBB API'den grup üyeliği doğrulama
844
+ - Admin panel üzerinden manuel kontrol