
E-Ticaret

Bergaz Gıda için geliştirdiğim kapsamlı e-ticaret platformu. Kurumsal tanıtım, online satış ve müşteri yönetim sistemini tek çatı altında birleştiren modern bir çözüm.
Bu vitrin tek başına yazılmış bir site değil; kendi geliştirdiğim e-ticaret platformunun bir konuşlandırması. Aynı platform iki müşteride çalışıyor — Bergaz Gıda ve Ezine Gurme. Ortak olan çekirdek: veri modeli, ödeme akışı, stok hareket defteri, rol sistemi ve panellerin tamamı. Müşteriye özgü olan kısım marka katmanı, içerik ve entegrasyon anahtarları. Bu ayrım baştan kurulmasaydı ikinci müşteri bir kopya repo demek olurdu; kopya repolarda düzeltilen bir hata yalnız bir tarafta düzelir.
Her sayfa aynı stratejiyle çalışmıyor; karar sayfanın tazelik ihtiyacına göre
veriliyor. Ana sayfa ISR ile 60 saniyede bir tazeleniyor ve altı Suspense sınırı
üzerinden akıyor. Ürün kataloğu dinamik: filtreler ve sayfalama URL'de duruyor,
böylece bir filtre kombinasyonu paylaşılabilir bir adres oluyor. Kurumsal
metinler (hakkımızda, gizlilik, iade, koşullar) günde bir tazelenen ISR ile
statik kalıyor. Yönetim ve satış panelleri tamamen dinamik — korumalı ve gerçek
zamanlı. Önbellek etiket bazlı geçersizleştiriliyor: bir ürün güncellendiğinde
tüm site değil, o ürüne bağlı etiketler tazeleniyor. Sepet ve karşılaştırma
listesi sunucuya değil, localStorage'a kalıcılaştırılan istemci store'larında
duruyor; oturum açmamış bir ziyaretçinin sepeti bu sayede kayboluyor değil, ama
fiyat ve stok ödeme adımında sunucuda yeniden doğrulanıyor — istemcideki hiçbir
sayı güvenilir kabul edilmiyor.
Uygulama tarayıcıdan kurulabiliyor ve son görülen katalog önbellekte kaldığı için bağlantı koptuğunda vitrin boş ekran vermiyor. Sipariş, kargo ve pazarlama e-postaları arka plan iş kuyruğundan gönderiliyor; ödeme akışı bir e-posta sağlayıcısının yavaşlamasıyla bloke olmuyor. Ziyaretçi analitiği üçüncü tarafa çıkmadan uygulama içinde tutuluyor.
Satış telefon ve mesajla yürüyordu; stok, sipariş ve tahsilat aynı yerde takip edilmiyordu. Bir e-ticaret platformunda zor olan kısım vitrin değil, ödeme ile stok arasındaki tutarlılık: ödeme sağlayıcısı aynı callback'i tekrar gönderirse, iki müşteri son adedi aynı anda sepete alırsa veya ödeme yarıda kalırsa stok ile sipariş birbirinden ayrışıyor. Bu ayrışma sessiz olur — kimse hata görmez, sadece sayılar tutmaz.
İki taraf. Alıcı: coğrafi işaretli peynir arayan son tüketici; gramaj ve ambalaj varyantı seçiyor, kargo eşiğine ne kaldığını görmek istiyor ve 3D Secure adımında güven arıyor. İşletme: ADMIN rolü ürün, stok, sipariş ve iade akışını yönetiyor; SALES rolü yalnızca satış tarafını görüyor, sistemin geri kalanına erişemiyor.
Tek geliştirici. Veri modeli, ödeme ve kargo entegrasyonları, stok hareket defteri, yönetim ve satış panelleri, arka plan iş kuyruğu, güvenlik sertleştirmesi ve Vercel dağıtımı.
Next.js 16 App Router, Neon PostgreSQL ve Prisma. Yazma yolu Server Action → Zod doğrulama → Prisma transaction. Ödeme akışı beş adım: sepet doğrulama (stok ve fiyat sunucuda yeniden kontrol edilir), iyzico 3D Secure başlatma, banka doğrulama yönlendirmesi, callback işleme ve sipariş sonlandırma. Sipariş kaydı, stok düşümü ve stok hareketi tek transaction içinde yazılıyor. Kargo ve e-posta işleri arka plan iş kuyruğuna giriyor. Görseller Vercel Blob'da tutuluyor. Kod tarafı yayına hazır; ana dal Vercel'de staging benzeri bir production olarak duruyor — iyzico ve kargo canlı anahtarları operatör tarafından devreye alınacağı için ödeme akışı şu an mock sağlayıcıyla çalışıyor.
Ödeme sağlayıcısı aynı callback'i tekrar gönderebiliyor. "Sipariş zaten ödendi mi" diye okuyup sonra yazmak, iki eşzamanlı callback arasında yarış bırakıyordu. Callback token'ı unique kısıtlı bir log tablosuna önce yazılıyor; ikinci callback bu yazmada düşüyor ve hiç işlenmiyor.
ÖdünleşimBaşarısız olanlar dahil her callback bir satır yazıyor; tablo sürekli büyüyor ve ayrı bir temizlik işi gerektiriyor. Ayrıca kısıt ihlalini hata değil "zaten işlendi" olarak yorumlayan özel bir kod yolu taşımak gerekiyor.
Stok kontrolü ile stok düşümü arasında geçen sürede başka bir sipariş son adedi alabiliyordu. Okuma, doğrulama, sipariş oluşturma, stok düşümü ve stok hareketi kaydı tek transaction'a alındı; ödeme başarısız olduğunda stok geri yükleme ve sipariş iptali de aynı şekilde tek transaction.
ÖdünleşimTransaction, ödeme sonlandırma boyunca ilgili varyant satırlarını tutuyor; aynı ürüne aynı anda gelen siparişler sıraya giriyor. Yoğun bir anda bu, bekleyen müşteri için doğrudan gecikme demek.
Yükleme klasörü adı yönetici girdisinden geliyordu; serbest bırakıldığında path traversal ve rastgele klasör kirliliği mümkündü. Kabul edilen altı klasör kodda sabit bir küme olarak tanımlı, dışındaki her değer hata veriyor.
ÖdünleşimYeni bir klasör açmak artık kod değişikliği ve dağıtım gerektiriyor; panelden yapılamıyor. İçerik tarafının esnekliği, saldırı yüzeyi karşılığında bilerek kısıldı.
Bot trafiği sadece giriş denemesi değil; üye kaydı, ürün yorumu, iletişim formu, bülten aboneliği ve sipariş sorgulama da hedef oluyor. Doğrulama bu uçların hepsinde sunucu tarafında kontrol ediliyor.
ÖdünleşimBu formların tamamı üçüncü taraf bir script'e bağımlı hale geldi; script yüklenemezse form gönderilemiyor. Her gönderim ayrıca bir doğrulama isteği maliyeti taşıyor.
Prisma, 31 modelli bir şemada ilişkileri ve migration geçmişini takip edilebilir tutuyor; ödeme ve stok tarafında ihtiyaç duyulan çok ifadeli transaction desteği hazır geliyor. Neon PostgreSQL dağıtımla aynı serverless modelde kalıyor. Vercel Blob görselleri repo ve veritabanı dışında tutuyor, görsel optimizasyonu remote pattern üzerinden çalışıyor. Rate limiter bellekte değil veritabanında tutuluyor — serverless'ta her örneğin kendi sayacını taşıması limiti anlamsız hale getiriyordu.
Altı HTTP güvenlik header'ı tanımlı: CSP, nosniff, çerçeveleme engeli, referrer politikası, HSTS ve kısıtlanmış Permissions-Policy. Kullanıcı girdisi (yorum, iade gerekçesi, sipariş notu, profil metni, iletişim formu) sanitize ediliyor. Rate limiting veritabanı destekli merkezi bir limiter üzerinden yürüyor. Üç rol var — ADMIN, SALES, USER — ve korumalı her Server Action oturumu ayrıca doğruluyor. Mock ödeme yalnızca sunucu tarafında okunabilen bir değişkenle kontrol ediliyor ve production'da açık bir geçiş izni olmadan reddediliyor. İade onayları ve stok hareketleri audit log'a yazılıyor.
React Compiler otomatik memoization açık; grafik bileşenleri dinamik import ile ana paketten ayrılıyor ve anasayfa Suspense sınırlarıyla akıyor. Server Action önbelleği etiket bazlı geçersizleştiriliyor, böylece bir ürün güncellemesi tüm sayfayı değil ilgili etiketi tazeliyor. Görseller AVIF ve WebP olarak sunuluyor, paket analizi repoda hazır bir komut olarak duruyor. 10 Playwright smoke testi ürün yaşam döngüsü, kupon ve iade akışlarını kapsıyor. Lighthouse mobil (kendi ölçümüm, 4 sıcak koşu medyanı): 59/100 — Eylül 2026. Skor düşük ve nedeni ölçümde açıkça görünüyor: engelleyen script değil ilk baytın gecikmesi — FCP medyanı 6,1 saniye, buna karşılık toplam engelleme süresi 36 ms. Vitrin sayfası her istekte sunucuda üretiliyor; asıl kazanç, ürün listesini istek başına render etmekten çıkarıp yeniden doğrulanan statik çıktıya taşımakta.
Ödeme ve stok tarafını bugün de aynı kurardım, ama sırayı değiştirirdim: idempotency kısıtını en baştan yazardım. Burada önce "zaten ödendi mi" kontrolüyle başladım ve yarış durumunu ancak sonradan kapattım — Görev Kahramanı'nda çift onay probleminde yaşadığımın aynısı, orada da garantiyi sonradan eklemiştim. İkinci olarak, Bergaz Analiz'de dönem kilidini uygulama katmanında bıraktığım için ödediğim bakım bedelini burada tekrarlamamak adına stok ve ödeme kurallarını daha erken veritabanına indirirdim. Üçüncüsü, ödeme mock'tayken alınan bir performans ölçümü gerçek yükü temsil etmiyor; canlı anahtarlar devreye girmeden ve tarih damgalı bir ölçüm almadan skor yayınlamanın anlamı yok.
Ne yapmak istediğinizi anlatın; ne kadar süreceğini ve nereden başlanacağını baştan söylerim.