Ana içeriğe geç
Projelere Dön
Bergaz Gıda logo

Bergaz Gıda

E-Ticaret

Kısaca

Önce
Satış telefon ve mesajla yürüyordu; stok, sipariş ve tahsilat aynı yerde takip edilmiyordu.
Sonra
Firma tanıtımı, online sipariş, stok ve sipariş yönetimi tek sitede; satış ekibi yalnızca satış ekranlarını görüyor, sistemin geri kalanına erişemiyor.
Ne değişti
Ödeme onayı iki kez gelse bile sipariş bir kez onaylanıyor ve iki müşteri aynı son ürünü aynı anda satın alamıyor; canlı ödeme hesabı işletme tarafından devreye alınacak.
Bergaz Gıda

Bergaz Gıda için geliştirdiğim e-ticaret sitesi. Firmanın tanıtımını, online satışı, stok ve sipariş takibini tek yerde topluyor.

Tek platform, iki konuşlandırma

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.

Dört panel, tek kod tabanı

  • Vitrin — ürün kataloğu, görsel galerisi, üç ürünü yan yana karşılaştırma, kupon ve toplu indirim destekli sepet, 3D Secure ödeme, blog, istek listesi ve son görüntülenenler.
  • Yönetim paneli — ürün ve varyant yönetimi, görsel yükleme, kargo takipli sipariş yönetimi, müşteri, kupon ve yorum moderasyonu, blog CMS'i, Soru-Cevap, mesaj kutusu, gelir grafikleri ve yapılandırılabilir ayarlar.
  • Satış paneli — satış personeli için daraltılmış görünüm: filtreli sipariş özeti, iade talebi işleme ve temel performans göstergeleri. Sistemin geri kalanına erişimi yok.
  • Müşteri hesabı — canlı durum takipli sipariş geçmişi, çoklu teslimat adresi, profil ve şifre yönetimi, tek tıkla iade talebi ve PDF fatura indirme.

Render ve önbellek stratejisi

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.

Platformun kendisi hakkında

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.

Kullanılan Teknolojiler

  • Next.js 16
  • TypeScript
  • Prisma
  • PostgreSQL
  • NextAuth
  • iyzico

Öne Çıkan Özellikler

  • Tam kapsamlı e-ticaret altyapısı
  • Güvenli ödeme entegrasyonu
  • Stok ve sipariş yönetimi
  • SEO optimizasyonu
  • Mobil uyumlu tasarım

Geliştirme Süresi

2,5 ay

Problem

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.

Kullanıcılar

İ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.

Rolüm

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ı.

Mimari

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.

Kritik Kararlar

  1. Ödeme callback'inde önce-yaz (insert-first) idempotency

    Ö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.

  2. Stok düşümü ile sipariş oluşturma aynı transaction'da

    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.

  3. Görsel yükleme klasörü için kapalı allowlist

    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ı.

  4. Turnstile yalnız girişte değil, tüm anonim yazma uçlarında

    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.

Teknoloji Seçimi

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.

Güvenlik

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.

Performans

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.

Sonuçlar

Şema
31 Prisma modeli · 32 migration
TypeScript kaynak dosyası
390
E2E test dosyası
10 (Playwright)
Aktif geliştirme
2026-05 → 2026-08 · 41 commit
Ödeme garantisi
Tekrar eden callback ikinci kez sipariş onayı üretemiyor; stok düşümü sipariş kaydıyla aynı transaction'da

Bugün Ne Farklı Yapardım

Ö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.

Nereden başlayacağınızı birlikte bulalım

Ne yapmak istediğinizi anlatın; ne kadar süreceğini ve en mantıklı ilk adımı baştan söylerim.