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: Laravel ve SEFAZ ile Çoklu Kiracı NF-e API’si Oluşturma: 6 Zor Düşünüm
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 » Laravel ve SEFAZ ile Çoklu Kiracı NF-e API’si Oluşturma: 6 Zor Düşünüm

Yazılım

Laravel ve SEFAZ ile Çoklu Kiracı NF-e API’si Oluşturma: 6 Zor Düşünüm

teknomers
Son güncelleme: 1 Mayıs 2026 06:01
teknomers
Paylaş
Paylaş

Brezilya, mal veya hizmet sunan her işletmenin NF-e (Nota Fiscal Eletrônica) düzenlemesini zorunlu kılar — bu, SEFAZ’a anlık olarak sunulan devlet imzalı bir XML’dir. Protokol SOAP tabanlıdır, 2006 yılında geliştirilmiştir, yalnızca Portekizce belgeleri mevcuttur ve homologasyon ortamı hafta sonları çevrimdışı kalır.

Benim için FoxNFe isimli, tüm bunları basit bir REST API’ye dönüştüren çok kiracılı bir SaaS oluşturmak 6 ay sürdü. İşte, zor yoldan öğrendiğim her şey.




Mimarisi

Tenant App ──REST──► FoxNFe Laravel API ──SOAP/XML──► SEFAZ (27 eyalet)
                                │
                         PostgreSQL RLS
                         Redis + Queues
                         Certificate Store
    
Ekranı tam ekran yap

Tam ekran modundan çık

  • Laravel 11 — API arka uç, kuyruklu işler, servis katmanı
  • PostgreSQL + RLS — her kiracı için satır düzeyinde güvenlik
  • Redis + Laravel Queues — asenkron işleme (SEFAZ her istek için 2-30 saniye alır)
  • A1 Dijital Sertifikaları — her kiracı için şifrelenmiş, çalışma zamanında yüklenen
  • 27 SEFAZ son noktası — Brezilya eyaletlerinin her biri için, CNPJ UF ile yönlendirilen



Ders 1: PostgreSQL RLS finally Zorunludur

PostgreSQL üzerinde çok kiracılı uygulamalar genellikle bir oturum değişkeniyle Satır Düzeyinde Güvenlik kullanır:

CREATE POLICY tenant_isolation ON tenant_plan_usages
      USING (
        tenant_id = current_setting('app.tenant_id')::int
        OR current_setting('app.bypass_rls', true) = );
    
Ekranı tam ekran yap

Tam ekran modundan çık

Admin sorgular için, kiracı sınırlarını aşması gerektiğinde RLS’yi bypass etmeli:

// DANGEROUS without finally
    DB::statement("SELECT set_config('app.bypass_rls', 'on', false)");
    $usage = TenantPlanUsage::withoutGlobalScopes()->where(, $id)->first();
    DB::statement("SELECT set_config('app.bypass_rls', '', false)");
    
Ekranı tam ekran yap

Tam ekran modundan çık

Gönderdiğimiz hata: set_config çağrıları arasında bir istisna, bypass_rls="on" değerinin veritabanı bağlantısı için aktif kalmasına sebep oldu. Bu bağlantı üzerinde yapılan her sonraki sorgu, tüm kiracılardan veri döndürdü.

Düzeltme:

public function getOrCreateUsage(Tenant $tenant): TenantPlanUsage
    {
        return DB::transaction(function () use ($tenant): TenantPlanUsage {
            DB::statement("SELECT set_config('app.bypass_rls', 'on', false)");
            try {
                $usage = TenantPlanUsage::withoutGlobalScopes()
                    ->where(, $tenant->id)
                    ->lockForUpdate()
                    ->first();

                if ($usage === null) {
                    $plan  = $this->getActivePlan($tenant);
                    $quota = $plan->isEnterprise() ? null : $plan->monthly_note_quota;
                    $usage = TenantPlanUsage::forceCreate([
                        => $tenant->,
                        => $plan->,
                        => 0,
                        => now()->startOfMonth()->toDateString(),
                        => now()->endOfMonth()->toDateString(),
                        => $quota,
                    ]);
                }

                return $usage;
            } finally {
                // İstisna oluşsa bile geri yükle
                DB::statement("SELECT set_config('app.bypass_rls', '', false)");
            }
        });
    }
    
Ekranı tam ekran yap

Tam ekran modundan çık

Kural: Her bypass_rls on için bir finally gereklidir. Kod inceleme süreci, bunu bir lint kuralı gibi zorunlu hale getirmelidir.




Ders 2: A1 Sertifikaları — Her şeyi Şifrele, Diskte Hiçbir Şey Yazma

Her Brezilyalı şirket, bir ICP-Brasil otoritesi tarafından çıkarılan dijital A1 sertifikasına sahiptir (.pfx dosyası, şifre korumalı). Bu sertifika, her NF-e XML’sini imzalar. Çok kiracılı bir ortamda, her kiracının kendi sertifikası bulunur.

NE YAPMAMALISINIZ: Sertifikaları disk üzerinde CNPJ ile adlandırarak dosya olarak saklamak veya şifrelenmiş içeriği herhangi bir yere kaydetmek.

NE YAPIYORUZ:

// Sakla — DB'ye kaydetmeden önce şifrele
    public function storeCertificate(Tenant $tenant, UploadedFile $pfx, string $password): void
    {
        $tenant->update([
            => encrypt($pfx->get()),
            => encrypt($password),
            => $this->extractExpiry($pfx->get(), $password),
        ]);
    }

    // Çalışma zamanında yükle — yalnızca bellekte şifrele, diske hiçbir şey dokunma
    public function loadCertificate(Tenant $tenant): array
    {
        $certData = [];
        $success  = openssl_pkcs12_read(
            decrypt($tenant->),
            $certData,
            decrypt($tenant->)
        );

        if (!$success) {
            throw new InvalidCertificateException(
                {$tenant->});
        }

        return $certData;// ['cert' => '...PEM...', 'pkey' => '...PEM...']
    }
    
Ekranı tam ekran yap

Tam ekran modundan çık

Ek güvenlik önlemleri:

  • encrypt() Laravel’in APP_KEY (AES-256-CBC) kullanır — anahtarları dikkatli bir şekilde döndür, yeniden şifreleme planla
  • certificate_expires indisi — kiracıları son kullanımdan 30 gün önce uyar, aksi takdirde NF-e yayını durur
  • .pfx dosyasını şifrelenmiş olarak diske yazma, /tmp bile dahil değil



Ders 3: SEFAZ cStat Kodları — Her Biri Önemlidir

SEFAZ, bir cStat alanı ile XML döndürür. Çoğu belge yalnızca 100’ü belirtir. İşte üretimde gerçekten göreceğiniz tüm harita:

cStatAnlamıDoğru eylem
100Yetkilendirilmiş ✅XML’i kaydet, kotayı artır
150Yetkilendirilmiş (zaman penceresi dışında)100 ile aynı
204Hali hazırda yetkilendirilmiş (duplikat)100 olarak değerlendir
110Reddedildi — veri hatasıXML’i düzelt, yeniden gönder
301Yetkisiz kullanım (yanlış UF son noktası)Eyalet yönlendirmesini kontrol et
539Şema doğrulama hatasıXML’iniz biçimsel olarak hatalı
999SEFAZ iç hataÜst üste geri çekme ile yeniden dene

cStat 204’ü tuzak olarak düşünün. Eğer işiniz zaman aşımına uğrarsa ve yeniden denemeler yaparsanız, SEFAZ NF-e’yi zaten işlemeye almış olabilir. 204’ü bir hata olarak değerlendirmek, kota içinde çift kayıt yapılmasına ve kiracıyı kafanızı karıştıracak biçimde yanıltacaktır. Her zaman 204’ü başarı olarak değerlendirin.

private function processResponse(NFeResponse $response): void
    {
        match (true) {
            in_array($response->, [100, 150, 204]) => $this->handleAuthorized($response),
            $response->=== 110                    => $this->handleDenied($response),
            $response->=== 999                    => $this->handleRetry($response),
            default                                     => $this->handleUnknown($response),
        };
    }
    
Ekranı tam ekran yap

Tam ekran modundan çık




Ders 4: Kota Artışı Yetkilendirmeden SONRA Olmalıdır

İletişim hatası: processResponse() yetkilendirilmiş NF-e’yi kaydetti ama incrementNfeUsage() çağrısını yapmadı. Sonuç: Her kiracı, sonsuza dek sonsuz bir kota aldı.

Düzeltme — artışı yetkilendirme sonrasında yap, ve öyle ki kayıt yapar ama hata vermez:

private function handleAuthorized(NFeResponse $response): void
    {
        // 1. Önce yetkilendirmeyi kalıcı hale getir
        $this->->update([
            => NFeStatus::AUTHORIZED,
            => $response->,
            => now(),
            => $response->,
        ]);

        // 2. Kota artışını yap — eğer bu başarısız olursa, NF-e zaten SEFAZ'da yetkilendirilmiştir. Geri dönüşü yok. Hatanın kaydını düş ve devam et.
        try {
            $this->->($this->);
        } catch (\Throwable $e) {
            Log::(, [
                => $this->->,
                => $this->->,
                => $e->(),
            ]);
        }
    }
    
Ekranı tam ekran yap

Tam ekran modundan çık

Neden hata yerine yakalamalıyız? NF-e artık SEFAZ’da yetkilendirilmiştir — hükümet sisteminde mevcuttur. Eğer buradan bir hata fırlatırsak, iş tekrar eder, SEFAZ 204 (“zaten yetkilendirilmiş”) döner ve sonsuz bir döngüye girmiş oluruz. Kiracı, yetkilendirilmiş NF-e’sine ulaşır ve işlemler, kotaları manuel olarak düzeltmek için bir uyarı alır.




Ders 5: Kiracı Çözümlemesinde Gizli IDOR

Kontrolcülerimiz, başlangıçta mevcut kiracıyı X-Tenant-ID başlığından çözümlemekteydi:

// VULNERABLE
    private function (Request $request): Tenant
    {
        return ::(->());
    }

    // upgrade() bunu kullanıyordu — her oturum açmış kullanıcı, HERHANGİ bir kiracının planını yükseltebiliyordu
    function ($request): {
        => ->();
        // ... faturalama yükseltme işlemini sürdür
    }
    
Ekranı tam ekran yap

Tam ekran modundan çık

Kiracı A için geçerli bir JWT’ye sahip bir saldırgan, X-Tenant-ID: B göndererek B’nin planını yükseltebilir, indirebilir veya kullanım verilerine erişebilir.

Düzeltme — tek satır, her zaman sahipliği doğrula:

private function ($request): {
        => ::(->());

        if (->()->!== ->) {
            (403, );
        }

        $tenant;
    }
    
Ekranı tam ekran yap

Tam ekran modundan çık

Basit. Ancak, çok kiracılı sistemlerde kiracı ID’sinin istemciden geldiği durumlarda, bu kontrolü varsaymayı unutmak kolaydır. JWT yeterli bir yetkilendirme değildir.




Ders 6: RefreshDatabase Olmadan Test Etme

Staging PostgreSQL’umuz DROP TABLE‘e izin vermiyor — güvenlik politikası. RefreshDatabase trait’i tamamen dışarıda. Açık setUp/tearDown kullanıyoruz ve doğrudan temizleme yapıyoruz:

PlanEnforcerTest TestCase
    {
        ?Tenant = ;

        function (): void
        {
            ::();

            (!->()) {
                ->();
                ; // koruma gerektirir — aşağıdaki tearDown notuna bak
            }

            ->=> ::()->();
        }

        function (): {
            // KRİTİK: setUp()'teki markTestSkipped() tearDown()'un çalışmasını engellemez
            // $this->tenant null ise (test atlanmış), forceDelete() hata verir. Her zaman null kontrolü yapın.
            (->!== ) {
                ::(, ->->)->();
                ->->();
            }

            ::();
        }

        function (): {
            // getOrCreateUsage içinde bir istisna simüle et
            ->(::)
                ->()
                ->(\RuntimeException());

            {
                (::)->(->);
            } ($e) {
                // Beklenen
            }

            => ::();
            ->(, ->, );
        }
    }
    
Ekranı tam ekran yap

Tam ekran modundan çık

Bu model — setUp’ta factory, tearDown’da forceDelete, her yerde null kontrolü — 11+ ay boyunca CI koşulları ile güvenilir bir şekilde çalıştı.




Bonus: Gönderimden Önce Yerel XSD Doğrulaması

SEFAZ cStat 539 (şema hatası) hangi alanın hatalı olduğunu size söylemez. Hata döngüsü: gönder → 5 saniye bekle → 539 al → neyin yanlış olduğunu tahmin et → tekrar et. Korkunç.

Çözüm: Elektronik imzalamadan önce resmi XSD’ye göre yerel olarak doğrulama yapın:

public function ($xml): {
        = \DOMDocument();
        ->();

        = ();

        if (!->()) {
            = ();
            new (
                . [0]->. . [0]->);
        }
    }
    
Ekranı tam ekran yap

Tam ekran modundan çık

Bunu imzalamadan önce ve sonra çalıştırın. SEFAZ XSD’leri portal.nfe.fazenda.gov.br portalında mevcuttur (4 tıklama derinliğinde gömülü).




Sırada Ne Var

FoxNFe, bu hafta SEFAZ resmi homologasyonuna katılıyor. Sertifika canlıya çıktığında:

  • Tüm 27 Brezilya eyaleti için çok durumlu yönlendirme (CNPJ’dan UF otomatik algılama)
  • NFS-e (hizmet faturaları) belediye API’leri aracılığıyla — her şehrin kendi protokolü var
  • Yetkilendirme, reddetme ve iptal bildirimleri için Webhook bildirimleri
  • Kota kullanımı, sertifika son kullanımı sayacı ve NF-e durum zaman çizelgesi ile bir показатьанmış

Brezilya’da mali entegrasyonlar oluşturuyorsanız veya SEFAZ, SOAP veya PHP sertifika yönetimi hakkında sorularınız varsa — yorum bırakmaktan çekinmeyin. Bu konulardan herhangi birine daha derinlemesine girmeye memnuniyetle açığım.

25 yıllık geliştirme deneyimi. Yapıldı: Laravel 11 · PostgreSQL 16 · Redis · Docker · Claude Code (Anthropic)

🔗 foxnfe.centralfox.online | Reddit u/foxdigitaldev

Kaynak: Orijinal Makale

Contents
  • Mimarisi
  • Ders 1: PostgreSQL RLS finally Zorunludur
  • Ders 2: A1 Sertifikaları — Her şeyi Şifrele, Diskte Hiçbir Şey Yazma
  • Ders 3: SEFAZ cStat Kodları — Her Biri Önemlidir
  • Ders 4: Kota Artışı Yetkilendirmeden SONRA Olmalıdır
  • Ders 5: Kiracı Çözümlemesinde Gizli IDOR
  • Ders 6: RefreshDatabase Olmadan Test Etme
  • Bonus: Gönderimden Önce Yerel XSD Doğrulaması
  • Sırada Ne Var
Sunucu Çökme Sorunlarını Önleme: Laravel SaaS Uygulamaları için Dinamik API Hız Sınırlama
Yandex Arama API’si Üzerine: yandex-search-php PHP Kütüphanesi İncelemesi
Laravel Artık Yerel Passkey’lere Sahip: laravel/passkeys için Kapsamlı Bir Rehber
Daha Büyük Sunucular Satın Almayı Bırakın: Laravel Yük Dengeleyici Mimarisi Kurma
Dokufy: İhtiyaçlarınıza göre PDF oluşturma — Gotenberg, LibreOffice veya yerel PHP.
Bu Makaleyi Paylaş
Facebook Bağlantıyı Kopyala Yazdır
Paylaş
Önceki Makale ChatGPT Görseller 2.0 Hindistan’da Büyük İlgi Görüyor, Diğer Yerlerde Henüz Başarılı Değil
Sonraki Makale Doktorlar, AI’dan İkinci Bir Görüş Almalı mı? Reid Hoffman’ın Tartışması

Sanal Medya

FacebookBeğen
452Takip Et
PinterestSabitle
237Takip Et

Son Eklenenler

OpenAI Hassas Verileri Koruma İçin Lockdown Modunu Tanıttı
Genel
RAM fiyatları yıl sonuna kadar iki katına çıkacak, indirimler eski stokları eritmekten kaynaklanıyor
Donanım
Meta Kendi Yapay Zeka Tabanlı Tıklama Tuzağı Haber Akışını Yaratıyor
Liste
Final Fantasy 7 Minigame Yenilikleriyle Seçim Heyecanı Sunuyor
Oyun
1972’de 8 inçlik, 80KB’lik disklerin patenti alındı
Donanım
GOG Nazi Sembolleriyle İlgili E-Posta Göndermek Üzere Özür Diledi
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?