Bugün v0.9.0 sürümünü LaraFoundry için etiketledim: faturalama. Ancak dürüst bir başlık, faturalama ekini değil, faturalama şemasını gönderdiğimdir. Ücretsiz çekirdek artık bir abonelik sistemi, bir ödeme geçidi sözleşmesi, bir sürücü yöneticisi, abonelik sütunları üzerinde gerçek bir erişim kapısı ile şekillenmiş durumda ve tek bir kuruş bile almaz. Bu kasıtlıdır ve bu aşamanın en ilginç kısmı da budur.
LaraFoundry, kendimden bir canlı CRM’den halk içinde çıkardığım bir SaaS çekirdeğidir, her seferinde bir modül. Başlangıçta kendime koyduğum şart: çekirdek ücretsizdir ve para dışında her şey için ücretsiz kalır. Auth, çoklu kiracılık, RBAC, admin konsolu, etkinlik günlüğü, i18n, dosyalar: hepsi ücretsiz. Bir işletme müşterilerinden ücret talep etmek istediği gün, o da ücretli kısımdır. Yani faturalama sadece “başka bir modül” olamaz. Temiz bir şekilde ayrılması gerekiyordu; ücretsiz tarafta gerçek, kullanışlı bir yapı ve ücretli tarafta gerçekten para hareket ettiren kısımlar olmalıydı.
Taşımak istemediğim donör alışkanlığı
Taşımak istemediğim donör alışkanlığı
Bir şirket “aboneliği için ödendiğinde” orijinal CRM’nin yaptığı şey şuydu:
// TODO: gerçek ödeme geçidi entegrasyonu
// GEÇİCİ: her ödeme başarılıdır, test için
$paymentStatus = 'success';
O success hardcoded idi. Stripe, Paddle veya hiç bir geçit yoktu. “Ödeme yapmak” company_payments tablosuna bir satır yazıyor ve abonelik tarihini ileri alıyordu. Kendim yönettiğim bir CRM için, tek bir gerçek kullanıcıyla, bu yeterliydi: gerçek şeye ihtiyaç duymamıştım, bu yüzden yer tutucu orada sonsuza kadar durdu.
Artık bu reusable bir çekirdek haline geldiğinde, o yer tutucu zehir gibi oldu. Sürekli doğru olan bir success, gerçek bir geçidin olmayışından daha kötüydü çünkü faturalamanın çalışıyormuş gibi görünmesini sağlıyordu. Bu aşama için kural basit: sahte geçit çıkarılmayacak. Onun yerinde ne varsa, para almadığı gerçeğiyle dürüst olmalıdır.
Ekin gerçekten ne olduğu
Ekin gerçekten ne olduğu
Ücretsiz çekirdek PaymentGatewayInterface taşır: abone ol, iptal et, iade et, durum kontrol et ve webhook’u doğrula. Bu yalnızca para hareket ettirmenin mekaniği hakkında bilgi verir. Vergi veya faturalama hakkında hiçbir şey söylemez çünkü bu sorumluluk geçit türlerine göre farklılık gösterir (Stripe gibi bir PSP ile müşteri, kayıtlı satıcıdır ve KDV’yi üstlenir; Paddle gibi bir kayıtlı satıcı ile sağlayıcı bunu üstlenir). Bir geçit yöntemine vergi koymak, her ikisinde de yanlış bir model oluşturmak anlamına gelir.
Çekirdek, bu sözleşmeye karşı tam olarak bir sürücü kaydeder: null geçidi. Her para işlemini reddeder, güçlü bir şekilde:
public function subscribe(Tenant $tenant, string $planId, string $period, array $options = []): array
{
throw $this->notConnected();
}
Sessiz bir success yok. Eğer bir gerçek sürücü bağlı olmadan birisi onu çağırırsa, hızlı bir şekilde hatalar verir ve add-on’u yüklemenizi söyler. Sürücü, Laravel’in Mail veya Queue yöneticisi tarzında bir yönetici tarafından çözülür: bir yapılandırma anahtarı sürücüyü seçer, add-on gerçeklerini extend() aracılığıyla kaydeder, bu geçitlere erişemeyen bir ülkedeki bir ana bilgisayar kendi yerel PSP’sini kaydedebilir. Sağlayıcıları değiştirmek bir yapılandırma değeri gerektirir ve çağrı yerleri hangi geçidin kullanıldığını bilmez. O geçit-bağımsız şekil, benim düşündüğüm gibi çoğu Laravel SaaS başlangıçları için önemlidir: çoğu yalnızca Stripe’a bağlıdır ve Stripe yaklaşık 46 ülkeye ulaşmaktadır.
Gerçek işi yapan kapı
Gerçek işi yapan kapı
Faturalama işlemlerinin gerçekten ücretsiz çekirdek içinde yer alan tek parçası erişim kararına aittir. Bu aşamadan önce, Company::hasAccess() bir stub’dı ve true döndüren bir yer tutucuydu, faturalama geldiğinde çağrı yerlerinin değişmemesi için RBAC aşamasına yerleştirilmişti. Şimdi gerçek durumu okumaktadır:
public function hasAccess(): bool
{
if (! LaraFoundryBilling::enabled()) {
return true;
}
return $this->subscriptionState()->hasAccess();
}
Faturalama devre dışı olduğunda (varsayılan), daima true döner. Ücretsiz çekirdek, bir ödeme duvarı olmaksızın tam bir çoklu kiracı uygulamasıdır ve bu tüm vaaddir. Faturalama açıldığında, abonelik sütunlarını okur: bir deneme süresi veya aktif abonelik erişim verir; başka bir şey reddeder. Devre dışı olduğunda kapanır, devre dışıyken tamamen açıktır.
Abonelik sütunları (trial_ends_at, subscription_ends_at, plan_id, …) ücretsiz çekirdeğin migration’ında bulunmaktadır, add-on’da değil. Bu, bilinçli bir tercihtir: kapı, herhangi bir add-on yüklenmeden gerçek durumu okumalıdır ve bir ücretsiz kendi barındırma çözümü, denemeyi el ile verebilmelidir. Add-on yalnızca o sütunları yazar (webhook’u subscription_ends_at güncel tutar). Kendi başına çekirdek tablosuna herhangi bir sütun eklemez. Ve bu sütunlar topluca atanabilir değildir, bu nedenle bir kiracı asla yolla ücretsiz bir yıl aboneliğe erişemez.
Dürüstlük bölümü, çünkü her sürümün bir tane olur
Dürüstlük bölümü, çünkü her sürümün bir tane olur
En dikkatli olduğum şey: bu aşama kapıyı bağladı ama henüz hiçbir çağrıcı bunu sorgulamıyor. Tasarlanmış döngü, RBAC politika kontrolörü veya “abonelik gerektirir” middleware’ın hasAccess()‘i sormalıdır, böylece bir isteğin geçmesine izin verilmektedir. O bağlama bu aşamada yok. Yani bugün, faturalamayı etkinleştirmek kapının doğru yanıt vermesini sağlar, ancak çekirdekte bunun uygulanmasını sağlamak yoktur. Şu an gerçek durumu döndürmek, gelecekteki çağrı yerlerinin geldiğinde değişiklik gerektirmeyecek şekilde tasarlanmıştır; bu da iki aşama önce yerleştirilen stub’ın temel amacıdır. Ama “ücret duvarı kendini dayatır” henüz iddia edebileceğim bir şey değil, onu gündeme getirmiyorum.
Ve elbette, burada gerçek ödemeler yok. Hiçbir Stripe, Paddle, Cashier, hatta bağımlılık olarak bile yok. Hiçbir plan, promosyon kodu, deneme UI’sı, faturalama portalı, gelir ölçümü yok. Bunların hepsi ücretli larafoundry-billing add-on’udur, ayrı bir paket, sonraki bir aşama. Gönderilen şey, bu şeylerin üzerine inşa edildiği sözleşmedir.
Yine iplik
Yine iplik
Bu serideki her aşama aynı şekle ulaşmaktadır. İlginç mühendislik iyi; ders, bir uygulama için doğru olan bir varsayılan veya alışkanlığın, reusable bir uygulama için yanlış olmasıdır. Bu sefer sorun, hardcoded olan bir success: bir kullanıcı ile CRM için kusursuz çalışan ve başka birinin dağıttığı bir çekirdek içinde sessiz bir felaket olacak bir yer tutucu. Çözüm, gerçek bir geçit inşa etmek değildi. Onun yerine bir geçidin olmayışını ve bunu ifade eden bir bağlantı yapmaktı: para almayan ve bunu beyan eden bir ek.
Faturalama motoru, add-on’dur. Temel yapının gerçekten ücretsiz olması için ücretsiz/ücretli sınırını çizmek işin temeli olmuştur.
Takip et
Takip et
Kaynak: Orijinal Makale


