# FAZ 3 KAPANIŞ — Para Akışı Uçtan Uca

Para akışının dört eksik parçası tamamlandı. Bu dokümanda ne yazıldığı, nasıl
devreye alınacağı ve **sandbox'ta neyin doğrulanması gerektiği** var.

---

## 1. Akışın tamamı

```
① ÖDEME
   Sepet → kırılımlı iyzico isteği (subMerchantKey + subMerchantPrice)
   Token mg_checkout_sessions'a yazılır
   Fiş YOK — para iyzico havuzunda, satıcı alacağı henüz doğmadı

② MUTABAKAT              magostore:reconcile-payments  (her 10 dk)
   Token ile CheckoutForm::retrieve → paymentTransactionId'ler
   mg_settlement_items satırlarına yazılır
   Bu adım atlanırsa onay YAPILAMAZ, satıcı ödeme almaz

③ TESLİM                 ecommerce_order_status_updated → COMPLETED
   Hakediş fişi yazılır, satıcı alacağı doğar
   Vade = teslim + 3 iş günü (yasal sınır 5)
   Kırılımlar 'approvable' olur

④ ONAY                   magostore:approve-due  (her iş günü 09:00)
   iyzico item/approve → para satıcıya
   Satıcı alacağı kapanır

⑤ İADE                   ecommerce_order_return_status_updated → completed
   YOL A: onay verilmemişse → disapprove + ters kayıt, borç YOK
   YOL B: onay verilmişse   → ters kayıt, borç doğar, sonraki hakedişten mahsup
```

**③'ün ①'de değil teslim anında olması yasal bir zorunluluk.** 6563 s. Kanun
Ek m.1/2-a ödeme yükümlülüğünü "satış bedelinin tasarrufa girdiği **ve**
siparişin alıcıya ulaştığı" tarihe bağlıyor — iki koşul birlikte. Ödeme anında
fiş yazsaydık, teslim edilmemiş siparişler satıcı bakiyesinde görünürdü.

---

## 2. Simülasyonla doğrulandı

Defter mantığını ayrı bir ortama port edip senaryoyu çalıştırdım. En kritik
senaryo — **para ödendikten sonra gelen iade**, ki 5 iş günü kuralı yüzünden
bu normal yol:

| Adım | Satıcı alacağı |
|---|---|
| Sipariş A teslim (1.000 ₺) | 796,67 ₺ |
| Sipariş B teslim (600 ₺) | 1.256,67 ₺ |
| A ve B onaylandı, para gitti | 0,00 ₺ |
| **B iade edildi** | **−460,00 ₺** → borç doğdu |
| Sipariş C teslim (712,50 ₺ net) | 252,50 ₺ |

Son satır kritik: `712,50 − 460,00 = 252,50`. **Mahsup otomatik oldu**, ayrı bir
mahsup kodu yazmaya gerek kalmadı. Çünkü borç ve alacak aynı hesapta (`2000`)
yaşıyor; işaret farkı işi yapıyor.

Defter bütünlüğü: toplam borç 4.356,67 = toplam alacak 4.356,67. Komisyon geliri
237,50 ₺ çıktı, yani B'nin komisyonu iade sırasında geri alındı (beklenen: A+C
komisyonu = 237,50 ✓).

İki koruma da çalışıyor: dengesiz fiş reddediliyor, aynı fişin ikinci kez ters
kaydı engelleniyor.

---

## 3. Yazılan kod

**25 PHP dosyası.** Yapısal denetim temiz, tüm iç bağımlılıklar çözülüyor,
migration'ların hepsinin `down()` kapsaması tam.

| Katman | Dosya | Görevi |
|---|---|---|
| Defter | `LedgerPoster` | Tek yazma yolu, atomik, dengesizi reddeder |
| | `LedgerEntryDraft` | Akıcı fiş kurucu + denge doğrulaması |
| Hakediş | `SettlementLifecycle` | Teslim ve iade akışları |
| | `SettlementItemRecorder` | Token → paymentTransactionId |
| | `ItemSettlementCalculator` | Kırılım hesabı + stopaj |
| | `OrderChargeAllocator` | Kargo yoğunlaştırma |
| | `MarketplaceBasketBuilder` | Kırılımlı sepet |
| | `CommissionRateResolver` | Üç kademeli komisyon |
| | `RevenueProjector` | Defter → mevcut panel |
| PSP | `IyzicoMarketplaceClient` | Alt üye, onay, onay kaldırma |
| | `StoreSubMerchantSynchronizer` | Mağaza → iyzico |
| Destek | `BusinessDayCalendar` | İş günü, resmî tatil |
| | `Iban` | mod-97, hesap sahibi eşleştirme |
| Komut | `magostore:reconcile-payments` | Kırılım eşleştirme |
| | `magostore:approve-due` | Onay motoru |
| | `magostore:sync-submerchants` | Alt üye senkronizasyonu |
| Bağlantı | `MagostoreServiceProvider` | Hook kayıtları, ayrı log kanalı |

### Eklenti dosyalarına dokunulmadı

`fob-iyzipay` ve `marketplace` eklentilerinin hiçbir dosyası değiştirilmedi.
Bağlantı Botble'ın hook sistemi üzerinden. `remove_filter()` de kullanılmadı —
Botble'ın implementasyonu hook'un **tüm** dinleyicilerini siliyor, sadece bir
tanesini değil; Stripe ve PayPal de aynı hook'ta olduğu için onları da silmiş
olurduk.

### mp_customer_revenues silinmedi, projeksiyona dönüştü

Akış tek yönlü: `mg_ledger_lines` (gerçek) → `mg_store_balances` (önbellek) →
`mp_vendor_info` (projeksiyon). Ters yöne asla yazılmaz. Böylece satıcı
panelinin mevcut ekranları çalışmaya devam ediyor.

Bakiye önbelleğini **artımlı değil, her seferinde defterden yeniden**
hesaplıyorum. `available = available - X` biçimi bir kez kayarsa sonsuza kadar
kayar ve fark edilmez. Yavaş ama kendini onaran yaklaşım — para için doğru
takas bu.

---

## 4. Devreye alma sırası

```bash
cd ~/magostore

# 1) migration'lar — SIRAYLA
php artisan migrate

# 2) sağlayıcıyı kaydet: bootstrap/providers.php içine
#    App\Providers\MagostoreServiceProvider::class,

php artisan optimize:clear && php artisan config:cache

# 3) resmî tatilleri gir (Resmî Gazete'den doğrulanmış tarihlerle)
#    mg_public_holidays — bkz. 06-FAZ3c dokümanı

# 4) mağazaları iyzico'ya kaydet
php artisan magostore:sync-submerchants

# 5) kuru çalıştırmalar
php artisan magostore:reconcile-payments --limit=5
php artisan magostore:approve-due --dry-run
```

### Cron

```
*/10 * * * *   php /home/KULLANICI/magostore/artisan magostore:reconcile-payments
30 8 * * *     php /home/KULLANICI/magostore/artisan magostore:sync-submerchants
0 9 * * 1-5    php /home/KULLANICI/magostore/artisan magostore:approve-due
* * * * *      php /home/KULLANICI/magostore/artisan schedule:run
```

Sıra önemli: mutabakat onaydan önce çalışmalı, senkronizasyon her ikisinden önce.

---

## 5. Sandbox'ta doğrulanması ZORUNLU olan üç şey

Bunlar kodun varsayımları. Sandbox'ta bir gerçek ödeme yapılmadan canlıya
çıkılmamalı.

**① `getItemId()` bizim gönderdiğimiz değeri aynen döndürüyor mu?**
Kırılım eşleştirmesi `order_product_id`'yi `itemId` olarak gönderip dönüşte aynı
değeri okumaya dayanıyor. iyzico bu alanı normalize ediyorsa (kırpma, tip
dönüşümü) eşleştirme mantığı değişir. Kod eşleşmeyeni `critical` logluyor,
sessizce geçmiyor — ama önce görmek gerekir.

**② `disapprove` transfer sonrası ne döndürüyor?**
YOL A / YOL B ayrımı buna bağlı. Kod hatayı yakalayıp YOL B'ye düşüyor, yani
yanlış varsayımda bile para kaybı olmuyor; ama davranışı bilmek gerekiyor.

**③ Stopaj matrahı: iyzico dokümanındaki örnek bizim formülle uyuşmuyor.**
Doküman 100 TL için "KDV hariç 80 TL" alıyor; %20 KDV'de doğrusu 83,33 TL.
Fark oransal olarak %4,1. `withholdingTax` iyzico tarafında hesaba girmediği
için ödeme akışı bozulmuyor, ama **beyan edilen tutar bu.**
entegrasyon@iyzico.com ve mali müşavir teyidi gerekiyor.

Ayrıca: sandbox hesabının **pazaryeri olarak tanımlanması** iyzico'nun manuel
işlemi. entegrasyon@iyzico.com'a bugün yazılmalı, kod hazır ama test
edilemez durumda kalmasın.

---

## 6. Faz 3'te bilerek bırakılanlar

| Eksik | Neden bekliyor |
|---|---|
| Kargo entegrasyonları (Yurtiçi, Aras, MNG, Sürat, PTT) | Bağımsız iş, para akışını etkilemiyor |
| e-Arşiv / e-Fatura | Entegratör seçimi bekliyor |
| Taksit tablosu (BIN sorgulama) | SDK'da `InstallmentInfo` hazır, checkout'a bağlanacak |
| Kapıda ödeme | Hizmet bedeli ve risk devri kuralı kararı bekliyor |
| Mutabakat dosyası okuyucu | `mg_psp_settlement_rows` tablosu hazır, okuyucu yazılacak |
| ETBİS kaydı | İdari |

---

## 7. Projenin bugünkü durumu

| Faz | Durum |
|---|---|
| 0 — Temizlik ve marka | ✅ Betikler hazır, uygulanacak |
| 1 — cPanel kurulumu | ✅ 11 adımlık rehber hazır |
| 2 — İş kuralları | ⏳ 11 ticari karar onay bekliyor |
| 3 — Para akışı + iyzico | ✅ Kod tamam, sandbox doğrulaması bekliyor |
| 4 — Ortak katalog + buybox | ⏸ Tasarımı yapıldı, kodu yazılmadı |
| 5 — Hakediş v2 (mutabakat, ekstre) | ⏸ |
| 6 — Reklam | ⏸ |
| 7 — Fraud, uyuşmazlık, SLA | ⏸ |
| 8 — Yapay zekâ | ⏸ |
| Tasarım | ✅ 4 ekran + jeton sistemi |

**Canlıya çıkmanın önündeki gerçek engeller** — teknik olmayanlar dahil:

1. Satıcı sözleşmesindeki **ticari temsilci maddesi** (avukat)
2. Faz 2'deki **11 ticari karar** (yönetim)
3. **Sandbox pazaryeri tanımı** (iyzico, manuel)
4. **Stopaj matrahı teyidi** (mali müşavir)
5. **Kargo entegrasyonu** — en az bir firma
6. **ETBİS kaydı** (idari)

Bunların dördü kod dışı ve paralel yürütülebilir. Yazılım tarafında sıradaki
en değerli iş Faz 4 (ortak katalog + buybox) — tasarımı hazır, tabloları
kurmaya başlanabilir.
