Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Yazı Tipi BoyutlandırıcıAa
  • Anasayfa
  • Teknoloji
    • Siber Güvenlik
    • Yapay Zeka
    • Donanım
    • Bilim
  • Yazılım
  • Savunma & İstihbarat
  • Oyun
  • Yaşam
    • Finans
    • Sinema
    • Dünyadan Haberler
  • İş Birliği
Okuma: Herkese Açık Olarak Bir SaaS Motoru Geliştirmek: Stripe’a Bağlı Olmayan Bir Faturalama Motoru
Paylaş
Yazı Tipi BoyutlandırıcıAa
Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Ara
Bizi Takip Et
  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti
© 2026 Teknomers. All Rights Reserved.

Anasayfa » Herkese Açık Olarak Bir SaaS Motoru Geliştirmek: Stripe’a Bağlı Olmayan Bir Faturalama Motoru

Yazılım

Herkese Açık Olarak Bir SaaS Motoru Geliştirmek: Stripe’a Bağlı Olmayan Bir Faturalama Motoru

teknomers
Son güncelleme: 9 Haziran 2026 13:20
teknomers
Paylaş
Paylaş

Son zamanlarda, faturalama semesi üzerinde çalıştım ve bunun yalnızca faturalama olmadığını vurguladım. Ücretsiz çekirdek, bir abonelik sisteminin tüm şekline, bir geçit sözleşmesine, bir sürücü yöneticisine, gerçek bir erişim kapısına sahipti ancak tek bir kuruş bile almadı. Bu kasten yapıldı. Para alma kısmı ayrı bir ücretli eklenti olarak yer alıyor ve bu makale, o eklentinin Lean MVP’sini tamamlamakla ilgili.

<p>LaraFoundry, üretimde zaten çalışan bir CRM'den kamuya açık olarak çıkardığım bir SaaS çekirdeğidir, bir modül bir seferde. Çekirdek ücretsizdir ve para dışında her şey için ücretsiz kalır. Auth, çok kiracılık, RBAC, admin konsol, etkinlik günlüğü, i18n, dosyalar: hepsi ücretsizdir. Bir iş, kendi müşterilerinden ücret almak istediğinde, o kısım ücretli hale gelir. Bu nedenle faturalama "sıradan başka bir modül" olamazdı. Temiz bir çizgiyi takip etmeliydi: ücretsiz taraf yapıyı taşırken, ücretli taraf gerçek parayı hareket ettiren parçaları taşımaktadır.</p>

<p>"Lean MVP tamamlandı" demek, somut olarak şunu ifade eder: Gerçek fiyatlarla planlar. Gerçek bir barındırılan ödeme sayfasını açan bir ödeme sayfası. Promo kodları ve özgür bir ilk ay. Para kalmadığında erişimi engelleyen abonelik zorunluluğu. Sahipler için faturalama portalı ve ödemeleri izlemek ve promo kodlarını yönetmek için bir süper admin konsolu. Bir abonelik sona ermeden önce hatırlatıcı bir cron. Ve her satırı şekillendiren tek bir karar: hiçbiri tek bir ödeme sağlayıcısına bağlı değil ve hiçbir şey sabit bir para biriminde fiyatlandırılmamış.</p>

<h2>
    <a name="the-constraint-that-shaped-everything" href="#the-constraint-that-shaped-everything"></a>
    Her şeyi şekillendiren kısıtlama
</h2>

<p>Çoğu Laravel SaaS başlangıcı yalnızca Stripe kullanıyor. Stripe mükemmel ve pek çok geliştirici için doğru ve tek cevap. Ama Stripe yaklaşık 46 ülkeye ulaşabiliyor ve ben bu makale için başka bir pazar oluşturuyorum. Eğer Stripe’ı çağrı noktasına dahil edersem, yalnızca Stripe'ın hizmet verdiği ülkelere hitap eden bir çekirdek oluşturmuş oluyorum.</p>

<p>Kendime verdiğim görev, almak zorundaydı: Ödeme işlemini, ev sahibi hangi sağlayıcıyı kullanacaksa, bilmiyormuş gibi tasarlamak ve ev sahibinin müşterilerinin hepsinin dolar ile ödeme yapmadığını varsaymak. Aşağıda, bunu ciddiye almanın sonuçları ortaya çıkıyor.</p>

<h2>
    <a name="one-contract-many-drivers" href="#one-contract-many-drivers"></a>
    Tek sözleşme, çok sayıda sürücü
</h2>

<p>Ücretsiz çekirdek, yalnızca parayı hareket ettirme mekaniklerini tanımlayan tek bir sözleşme ile gelir:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="kd">interface</span> <span class="nc">PaymentGatewayInterface</span>

{
public function subscribe(Tenant $tenant, string $planId, string $period, array $options = []): array;
public function cancel(Tenant $tenant, bool $atPeriodEnd = true): void;
public function refund(string $chargeReference, ?int $amount = null): void;
public function subscriptionStatus(Tenant $tenant): string;
public function verifyWebhook(Request $request): array; // doğrulanmış yük döner, eğer değilse hata fırlatır
}

Tam ekran moduna geç

        <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Tam ekran modundan çık</title>
        <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
        </svg>
    </div>
</div>
</div>

<p>Bu, tüm yüzeydir. İçinde olmayan şeye dikkat edin: vergi ile ilgili hiçbir şey, faturalama ile ilgili hiçbir şey. Bu eksiklik kasten yapılmıştır. Vergi sorumluluğu, sağlayıcının <em>türüne</em> göre değişiklik gösterir. Stripe gibi bir PSP ile iş, ticaret kaydıdır ve kendi KDV'sine sahiptir. Paddle gibi bir ticaret kaydı olan bir sağlayıcıda, vergi sağlayıcıya aittir. Eğer <code>chargeTax()</code> veya <code>invoice()</code> gibi bir şeyi geçit yönteminin içine dahil edersen, bu iki modelden birini diğerine yerleştirmiş olursun ki bu diğer half için yanlıştır.</p>

<p>Ücretli eklenti, o sözleşme uyarınca gerçek sürücüleri kaydeder, tıpkı Laravel'in kendi Mail veya Queue yöneticileri gibi:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="c1">// Eklentinin hizmet sağlayıcısında. Form, genel API'dir; sürücü gövdesi eklentidir.</span>

$manager->extend(‘stripe’, fn () => new CashierStripeDriver(/ … /));
$manager->extend(‘paddle’, fn () => new CashierPaddleDriver(/ … /));

Tam ekran moduna geç

        <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Tam ekran modundan çık</title>
        <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
        </svg>
    </div>
</div>
</div>

<p>Bir yapılandırma anahtarı, etkin sürücüyü seçer. Bir aboneliği başlatan veya durumunu okuyan çağrı noktaları, hangi ödeme geçidi aldıklarını bilmezler. Stripe veya Paddle'ın ulaşamadığı bir ülkede bir ev sahibi, aynı sözleşme karşısında kendi yerel PSP'sini kaydedebilir ve tek bir yapılandırma değeri ile değiştirebilir. Tüm ödeme sağlayıcısını değiştirmek bir yeniden yapılandırma değil, bir dizedir.</p>

<p>Her iki gerçek sürücü de Laravel Cashier üzerine inşa edilmiştir (Stripe için <code>laravel/cashier</code>, Paddle için <code>cashier-paddle</code>). Cashier ve Spark değil, çünkü Spark, görüşe bağlı bir uygulama faturalama UI'sıdır ve ben bir geçit, ön yüz değil, bir yapıya ihtiyaç duydum. Sürücü gövdeleri, webhook doğrulaması, sütun yansıtma, eklentinin özel kısmı olduğu için onları burada tanımlayacağım, dökmeyeceğim. Şekil şudur: sürücü, bir barındırılan ödeme işlemi açar ve bir yönlendirme geri döner ve bir webhook dinleyicisi sağlayıcının abonelik durumunu, şirketin kendi sütunlarına yansıtır; böylece uygulamanın geri kalanı, hangi sağlayıcının yazdığına bakılmaksızın tek bir sütun seti okur.</p>

<p>Stripe ve Paddle, aynı hayvan değil ve sözleşme bunu sızdırmadan emmek zorundadır. Stripe'ın <code>subscribe</code> yöntemi, yönlendirme için bir barındırılan checkout URL'si döner. Paddle'ın overlay'i, sunucu yönlendirmesi yerine JavaScript'ine bir yük verilmesi talep ediyor. İkisi de aynı yöntem imzasını ve dönüş şeklini tatmin ediyor ve çağrıcıları aynı şekilde ele alıyor. Sözleşmedeki tek doğru dikiş <code>refund</code>'dır: bir ticaret kaydı olarak, programatik bir geri ödeme, sağlayıcının kendi ayarlamaları akışı üzerinden geçer, bu nedenle bu yol, açık bir şekilde bir hata fırlatmak yerine göründüğü gibi değildir.</p>

<h2>
    <a name="plans-are-priced-per-currency" href="#plans-are-priced-per-currency"></a>
    Planlar para birimine göre fiyatlandırılır
</h2>

<p>"ABD öncelikli değil" meselesinin diğer yarısı paranın kendisidir. LaraFoundry'deki bir planın <em>bir fiyatı yoktur</em>. Bunun yerine bir para birimi başına fiyatı vardır:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="nv">$plan</span> <span class="o">=</span> <span class="nv">$plans</span><span class="o">-&gt;</span><span class="nf">find</span><span class="p">(</span><span class="s1">'business'</span><span class="p">);</span>

$plan->priceFor(‘month’, ‘EUR’); // 1900 (küçük birimler, 19.00 EUR)
$plan->priceFor(<span class=”s1>’year’, <span class=”s1>’PLN’); // 79000 (790.00 PLN)

Tam ekran moduna geç

        <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Tam ekran modundan çık</title>
        <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
        </svg>
    </div>
</div>
</div>

<p>Tek bir USD rakamı yok ve gösterim zamanında bir dönüştürücü takılı değil. Varşova'daki bir müşteri, kendi para biriminde bir ücretle faturalandırılır ve bu para birimi için aslında seçtiğim bir fiyat olur, dolar miktarının canlı FX-rounded yaklaşımları değil. Para birimi, plan tanımının bir parçasıdır, önceden belirlenmiştir, görünümde hesaplanmamıştır. <code>PlanContract</code> ve <code>priceFor</code> açık çekirdekte yaşar, böylece ücretsiz bir kendi barındıran, para birimi öncelikli şekli ücretsiz alır; ücretli eklenti, yapılandırmaya dayalı planları sağlar ve her planı ve para birimini doğru sağlayıcı fiyat kimliğine eşler.</p>

<h2>
    <a name="where-the-freepaid-line-falls" href="#where-the-freepaid-line-falls"></a>
    Ücretsiz/ücretli çizginin nerede bulunduğu
</h2>

<p>Bu, bütün serinin üzerinde sürekli geri döndüğüm kısımdır. Ücretsiz çekirdek, <em>seme</em> ile gelir: geçit sözleşmesi, şirket modelinde erişim kapısı ve abonelik sütunları (<code>trial_ends_at</code>, <code>subscription_ends_at</code>, <code>plan_id</code>). Faturalama kapalıyken, varsayılan olarak, kapı her zaman açıktır ve çekirdek, herhangi bir ödeme duvarı olmaksızın eksiksiz bir çok kiracılı uygulamadır. Ücretsiz çekirdeğin tüm vaadi budur.</p>

<p>Ücretli eklenti, semayı doldurur. Gerçek geçitleri bağlar, bu sütunları webhook'lardan yazar ve bir diğer döngüyü tamamlar: haklar. Bir SaaS'daki erişim, iki bağımsız sorudur ve bunları birleştirmek tipik bir hatadır. RBAC "bu kullanıcı yapabilir mi" sorusunu yanıtlarken, hak "bu şirketin planı bunun için ödedi mi" sorusunu yanıtlar. Bir yönetici, RBAC'de <code>production.view</code> tutabilirken, şirket üretilen modül için yalnızca plan almamış olabilir. Bu nedenle bir rota, aynı anda ikisiyle de kapatılır:<br/></p>

<div class="highlight js-code-highlight">
    <pre class="highlight php"><code><span class="nc">Route</span><span class="o">::</span><span class="nf">middleware</span><span class="p">([</span>
        <span class="s1">'can:production.view'</span><span class="p">,</span> <span class="c1">// RBAC: şirkette bunu yapma yetkisi olan kim</span>
        <span class="s1">'entitlement:production.module'</span><span class="p">,</span> <span class="c1">// faturalama: planın ne için ödendiğidir</span>
        <span class="p">])</span><span class="o">-&gt;</span><span class="nf">get</span><span class="p">(</span><span class="s1">'/production'</span><span class="p">,</span> <span class="nc">ProductionController</span><span class="o">::</span><span class="n">class</span><span class="p">);</span>

Tam ekran moduna geç

        <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Tam ekran modundan çık</title>
        <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"/>
        </svg>
    </div>
</div>
</div>

<p>Ücretsiz çekirdek, varsayılan olarak açık bir yetki çözücü gönderir. Eklenti, plan özelliklerini okumak için bunu yeniden bağlar ve planın satın almadığı her şeyi reddeder. Faturalama kapalıyken, tüm özellikler açıktır. Faturalama açıldığında, sunucu tarafında, istekten sahte bir şey olmadan, başarısız kapatılma ile çözümlenir.</p>

<p>Süresi dolmuş veya ödenmemiş bir aboneliğin yürütülmesi, ev sahibinin seçtiği iki mod içerir. Yumuşak mod, isteği geçirir ancak durumu ortaya çıkarır, böylece kilitlemeden önce bir hatırlatma yapabilirsiniz. Sert mod, engellenmiş bir sayfaya yönlendirir. Bir durum, her modda serttir: hesap sahibinin erişimi devre dışı bırakılırsa, blok tüm şirkete yayılır; çünkü bir şirket, yasaklı bir sahibin koltuğunda faaliyet gösteremez. Bir günlük cron, sahipleri, bir kaç gün sonra bir abonelik sona ermeden önce, tekil olarak uyarır, böylece kimse aynı hatırlatmayı bir kez daha almaz.</p>

<h2>
    <a name="the-surfaces-briefly" href="#the-surfaces-briefly"></a>
    Kısaca yüzeyler
</h2>

<p>MVP ile birlikte iki UI yüzeyi gönderildi. Sahip, bir faturalama portalı alır: fiyatlar, ödeme, ödeme geçmişi ve engellenen sayfa. Süper admin, bir konsol alır: ödemelerin okunabilir bir listesini (para birimine göre toplandığı, dolayısıyla dönüştürücü olmadan, çünkü para birimi başına gerçek bilgidir) ve promo kodları üzerinde tam CRUD yetkisi. Her iki yüzey de Inertia + Vue sayfaları olarak inşa edilmiştir, ev sahibinin yayımladığı aynı teslimat düzenini kullanarak, böylece eklenti ikinci bir derleme kaynağını çekmez.</p>

<h2>
    <a name="the-proof-because-every-release-claims-one" href="#the-proof-because-every-release-claims-one"></a>
    Kanıt, çünkü her sürüm bir tane iddia eder
</h2>

<p>Bunların hepsi Pest altında yer alıyor. Eklentide yalnızca 241 test. Burada testlere dayanıyorum çünkü güvenilmez bir faturalama kapısı, hiç kapı olmaktan daha kötüdür ve hata modları sessizdir. <code>null</code> sona erme tarihini yazan bir webhook dinleyicisi, ücretli bir müşteriyi sessizce devre dışı bırakır. Fail-open bir koruma, ücretli katmanı sessizce verir. Bu tür hataların bir kısmı tam olarak gönderilmeden önce bir test ya da karşıt değerlendirme turu ile yakalandı ve bu yazmanın tam argümanı budur.</p>

<h2>
    <a name="the-honesty-section" href="#the-honesty-section"></a>
    Dürüstlük bölümü
</h2>

<p>Bu serideki her yazının bir bölümü vardır ve bu bölüm dikkat çekiyor çünkü "faturalama tamamlandı" demek kolay bir şeydir.</p>

<p>Bu bir Lean MVP'dir, bitmiş bir faturalama ürünü değildir. Henüz bir affiliate programı yok; bu, planlı olarak sonraki bir hedef. Hak döngüsü, plan üyeliğini kontrol eder, "abonelik hala aktif mi" ekseni ayrı olarak zorlanmaktadır ve bu iki kontrolün aynı olmadığını belirtmiyorum.</p>

<p>Ve büyük bir şey: Tüm bunları inşa ettim ve test ettim, ancak henüz gerçek bir Stripe veya Paddle hesabında canlı bir ücretlendirme gerçekleştirmedim. Sürücüler, sağlayıcıların test yüzeylerinde ve bir dizi Pest testlerinde çalıştırılıyor, gerçek bir üretim ticaret hesabında gerçek bir kartla değil. O son miller, gerçek anahtarlar, gerçek bir webhook tüneli, gerçek bir abonelik, ayrı bir adımdır ki ben bunu henüz almayacağım ve motorun savaş test edilmiş olduğunu söylemeyeceğim.</p>

<p>Bir şey daha, neyin açık olduğuna. Çekirdek yapı açık kaynaktır ve tümünü okuyabilirsiniz: sözleşme, yönetici, erişim kapısı, hak çözücü, para birimi öncelikli plan şekli. Eklenti, ücretli ve tescilli bir pakettir, bu nedenle sürücü gövdeleri, webhook doğrulaması ve uygulama içi yürütme iç detayları burada tanımlanmıştır, dökülmemiştir. Ücretsiz çekirdeğin gerçekte ücretsiz kalması, o sınırın varlığına işaret etmektedir.</p>

<h2>
    <a name="the-thread-again" href="#the-thread-again"></a>
    Yine ip
</h2>

<p>Bu serinin her aşaması aynı şekle ulaşır: İlginç mühendislik oldukça güzel; ancak gerçek ders, bir uygulama için doğru olan varsayımın, yeniden kullanılabilir bir şey için yanlış olduğu üzerinedir. Faturalama için bu iki varsayım birden fazla. Tek tedarikçi, çünkü çıkardığım uygulama yalnızca bir tane gerektiriyordu. Tek para birimi, çünkü tek bir kullanıcı yalnızca bir ödeme yapıyordu. Her ikisi de mükemmel bir CRM için uygundu ama bu da yeniden kullanılabilir bir çekirdeği sessizce tek bir pazara kapatacaktı. Yapılması gereken şey, Stripe desteği oluşturmak değil. Faturalamayı, Stripe’ın bir değiştirilmesi gereken karlılığı olduğu ve fiyat etiketinin varsayılan olarak dolarda olmadığı şekilde inşa etmekti.</p>

<h3>
    <a name="follow-along" href="#follow-along"></a>
    Takip edin
</h3>

Kaynak: Orijinal Makale

Cache-Aside Deseni: Tembel Önbelleklemenin Sanatı
Karmaşık Testleri Durdur: Laravel Testlerinde Zamanı Dondur
Laravel Uygulamasını Özel PHP Framework’e Tek Bir Claude Code Oturumunda Taşıdım
Laravel ile Hastane Yönetim Sistemi Nasıl Geliştirdim
Mühendislik Pratikliği: Modern Muhasebe SaaS’ının Bir Araç Gibi Hissettirmesi Gereken Nedenler
Bu Makaleyi Paylaş
Facebook Bağlantıyı Kopyala Yazdır
Paylaş
Önceki Makale Bruvi Pod Kahve Makinesi’nde Yüzde 50 İndirim! Şimdi Alın!
Sonraki Makale Amazon Çalışanları Seattle’dan Yeni Veri Merkezlerine Ara Vermesini İstiyor

Sanal Medya

FacebookBeğen
452Takip Et
PinterestSabitle
237Takip Et

Son Eklenenler

Amazon Ember Artline İncelemesi: Şık Sanat Televizyonunun Özellikleri
Genel
E-scooter Girişimcisi Uzay Veri Merkezleri İçin 5 Milyon Dolar Topladı
Genel
4K hazır RTX 5070 oyun PC’sinde 550$ indirimle 1,449$!
Donanım
Riot, Üretilen AI ile Sıradışı Oyun Deneyimlerine Yelken Açıyor
Oyun
Modern Ağlarda Gizli Güvenlik Tehlikesi: Araçlar Arasındaki Kritik Çalışma
Siber Güvenlik
Philips Hue Bridge Pro ile Aydınlatmada Yeni Bir Dönem mi Başlıyor?
Liste
//

Siber güvenlik, yapay zeka ve savunma sanayiinden; finans ve sinema dünyasına uzanan geniş bir yelpaze. Teknomers; teknoloji, strateji ve yazılım dünyasını sade bir dille sizlerle buluşturuyor.

Kurumsal

  • Hakkımızda
  • Gizlilik politikası
  • Tanıtım Yazısı ve Backlink Hizmeti

Kategoriler

  • Teknoloji
  • Oyun
  • Sinema
  • Siber Güvenlik
  • Bilim
  • Finans
  • Dünyadan Güncel Haberler

Populer

  • TV'de Ücretsiz İzlenebilen Şifresiz Erotik Kanallar (2025 Güncel Frekans Listesi)

  • The Last of Us PC Kontrolleri: Hızlı Silah Değiştirme ve Tüm Tuşlar (2025)

  • Hogwarts Legacy'de Odaklanma İksiri Nasıl Yapılır?

Teknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor HaberleriTeknomers | Dünyadan Güncel Teknoloji | Oyun | Müzik | Film | Spor Haberleri
Bizi Takip Et
© 2026 Teknomers. All Rights Reserved.
Welcome Back!

Sign in to your account

Kullanıcı Adı veya E-posta Adresi
Şifre

Şifrenizi mi unuttunuz?