Kendi ödeme sistemimizin denetimi neleri ortaya çıkardı
İki kritik bulgu ve ikisi de bakacağımız yerde değildi. O zamandan beri düzeltilmiş hatalar hakkında açık bir rapor.

Ağustos 2026'da ödeme sistemimizi baştan sona inceledik. Bir şey olduğu için değil, sistem kısa süre önce yayına girdiği ve neyi gözden kaçırdığımızı bilmek istediğimiz için.
Epey şey kaçırmıştık. Bu makale bulguları tek tek sayıyor. Burada anlatılan her şey düzeltildi ve çoğu zaten herkese açık değişiklik günlüğünde yer alıyordu.
İki kritik bulgunun kaynağı olan modelleme hatası
Biri bizden Premium satın aldığında, ödeme sağlayıcımızda bir müşteri kaydı oluşturulur. Bu kaydı sunucu ID'si altında saklıyorduk, çünkü sonuçta Premium bir sunucuya yazılıyor.
Bu tam olarak yanlış model ve en ciddi iki bulgunun kökü buydu.
Bir müşteri kaydı bir alıcıya aittir. Onun ödeme yöntemini, fatura geçmişini ve biri iki sunucu için ödeme yaptığı anda birden fazla aboneliği taşır. Onu sunucu ID'sine göre anahtarlamak, birden fazla kişinin aynı satırı göstermesi anlamına gelir.
Bulgu 1: Başkasının müşteri verileri, ödeme yapmadan üzerine yazılabiliyordu
Satın alma, bir sunucunun her üyesine açık ve müşteri kaydını getiren ya da oluşturan fonksiyon ödemeden önce çalışıyor. Satırı sunucu ID'sine göre buluyor ve sessizce yeni adaya göre yeniden yazıyordu, ödeme sağlayıcısındaki e-posta adresi dahil.
Pratikte bu şu demekti: birinin bir noktada satın alma yaptığı bir sunucuda tek bir tık yeterliydi. Ondan sonra o ilk alıcının her aboneliğinin faturaları yabancının e-posta adresine giderdi. Ödeme yok, onay yok.
Herhangi bir yeniden kullanımdan önce kaydın gerçekten bu hesaba ait olup olmadığı kontrol edilerek düzeltildi. Uyuşmazlık olduğunda ayrı bir kayıt oluşturulur ve diğerine dokunulmaz.
Bulgu 2: Fatura portalı başkalarının aboneliklerini açığa çıkarıyordu
Aynı modelleme hatası, bir seviye ötede. Sahiplik kontrolü sunucunun aboneliğine uygulanıyordu, ama portal oturumu tasarım gereği birden fazla sunucu taşıyabilen müşteri kaydı için açılıyordu.
Yani bir sunucuda meşru şekilde satın alma yapan herkes, aynı kaydın diğer sunucular için taşıdığı abonelikleri görebiliyor ve yönetebiliyordu.
Müşteri kaydı sunucu satırından değil, ilgili satın alma işleminden çözülerek düzeltildi.
Kodun kendi koşullarımızla çeliştiği iki bulgu
Bizi en çok rahatsız eden kategori bu, çünkü teknik değil.
Kara liste ödenmiş süreyi elinden alıyordu. Koşullarımız, bir aboneliğin mevcut dönemin sonunda sona erdiğini söylüyor. Ama bir sunucu engellendiğinde kod anında iptal çağırıyordu: iade yok ve zaten ödenmiş bir dönem için o andan itibaren hizmet yok. Kod, yazılı olarak söz verdiğimizden farklı bir şey yapıyordu. Artık ödenmiş dönem sonuna kadar işliyor.
Cayma hakkından feragat kanıtlanamıyordu. Bir satın almadan önce hizmetin hemen başladığını onaylaman gerekiyor. Bu onay tamamen tarayıcıda yaşıyordu, hiçbir zaman gönderilmiyor ve hiçbir yerde saklanmıyordu. İspat yükü bizde ve elimizde basitçe hiçbir kanıt yoktu. Artık onay, ödeme rotasının zorunlu bir alanı, bir zaman damgası ve sürümlenmiş bir metinle saklanıyor ve metnin kendisi de sürümleniyor: mevcut bir metin asla düzenlenmez, her zaman yeni bir sürüm oluşturulur, aksi halde eski kayıtlar artık yeniden üretilemezdi.
İkinci tur birincisinden daha kötüydü
İlk 21 bulgudan birkaç hafta sonra tekrar baktık. Doğru karardı, çünkü en ciddi tek bulgu ancak o zaman ortaya çıktı.
B sunucusundaki bir iade, A sunucusunun ödenmiş ömür boyu lisansını siliyordu. İade ve ters ibraz bildirimleri bir sunucuyu değil, sadece bir ödemeyi belirtir. Bunları müşteri kaydı üzerinden çözüyorduk. Birinci turda bir kaydın birden fazla sunucuda bir alıcıya ait olduğunu doğru şekilde tespit ettikten sonra, tam da bu çözümleme artık tek anlamlı değildi. İki sunucu için ödeme yapan ve birinden iade alan biri, diğerinin ömür boyu lisansını kaybedebilirdi.
Çözümleme artık dört kesin adımdan geçiyor ve tek anlamlı olarak çözülemeyen her şey tahmin edilmek yerine bildiriliyor.
Kazanılan bir ters ibraz hiçbir şeyi geri getirmiyordu. Bir ters ibrazda kod satın alma satırını siliyordu, yani bu sunucunun hiç ömür boyu lisansı olduğunun tek kanıtını. Davayı daha sonra kazanmak bunu geri alamıyordu. Artık silmek yerine işaretliyor.
Veri saklama, ödeme yapılmış sunucuları siliyordu. Bot bir sunucudan ayrıldığında, verileri 30 gün sonra kaldırılır. Bu temizlik hiçbir zaman bağlı, devam eden bir abonelik olup olmadığını sormuyordu.
Bundan çıkardıklarımız
Hatalar ödeme kodunda değildi. Kritik bulguların hiçbiri bir tutar, bir para birimi ya da bir hesaplamayla ilgili değildi. Hepsi arkasındaki modeldeydi: bir müşteri kaydı kime ait, bir ödeme kime ait, bir sunucu kime ait. Bir ödeme sistemini tutarlara bakarak denetleyen herkes, tehlikeli kısımdan gözünü kaçırıyor.
Bir denetimin ikinci bir tura ihtiyacı var. En ciddi bulgu, birinci turdaki bir düzeltmenin doğrudan sonucuydu. Bu tesadüf değil: bir modeli düzeltmek, başka yerlerde hâlâ geçerli olan varsayımları geçersiz kılar.
"Koşullarımızda öyle yazıyor" kontrol edilebilir bir cümledir. İki bulgu, yazılı olarak söz verdiğimiz ile kodun yaptığı arasındaki farklardı. Bu okunabilir ve kimse okumaz, çünkü mühendislikten çok hukuk gibi görünür.
Satın alma koşullarının tamamı kullanım koşullarımızda, cayma hakkı ise cayma sayfasında.
Diğer yazılar
AlphaCloud'da neden neredeyse her şey ücretsiz kalıyor
Çoğu Discord botu en faydalı özelliklerini bir aboneliğin arkasına kilitler. Biz bilerek bunu yapmıyoruz ve bunun cömertlikle, göründüğünden daha az ilgisi var.
Perde ArkasıKodumuzun içindeki hukuk
Giriş yapmadan çalışması gereken bir fesih butonu. Seni asla reddetmemesi gereken bir form. Hukuki yükümlülükler bizim için neden kalıp metin değil, mimari.
Perde Arkası4.000 Discord sunucusunu takip etmek: Grafana, PostHog ve bize gösterdikleri
Trafiğimizin yarısına sessizce yol açan bir döngü, günde üç milyon gereksiz istek ve bir dakika sonra biten bir saldırı. İzleme sistemimiz neleri görüyor ve neleri bilerek görmüyor.