# API Platformu — Operasyon Notu

Geliştirici API'sinin (REST v1 + MCP) çalışması için gereken altyapı ve rutin bakım işleri.

## 1. Swetest binary (ZORUNLU)

Tüm harita hesapları `SwetestService` üzerinden Swiss Ephemeris binary'sini çağırır.
Binary yoksa servis constructor'da exception atar ve **tüm `chart.*` / `predictive.*` SKU'ları 500 döner**.

| Gereksinim | Değer |
|-----------|-------|
| Dizin | `storage/app/sweph/` |
| Binary (Linux) | `storage/app/sweph/swetest` (chmod +x) |
| Binary (Windows) | `storage/app/sweph/swetest.exe` |
| Ephemeris dosyaları | Aynı dizin (`*.se1`) |

Deploy sonrası kontrol:

```bash
ls -l storage/app/sweph/swetest && storage/app/sweph/swetest -h | head -1
```

`storage/` dizini git'e dahil değildir — yeni sunucuda binary + efemeris dosyaları elle kopyalanmalıdır.

## 2. Rate limiter cache'i (ZORUNLU)

`api-platform` rate limiter'ı (bkz. `AppServiceProvider::configureApiPlatformRateLimiter`) plan bazlı
dakikalık limiti cache üzerinden sayar. Limit key: `api-key:{id}`, key çözülemezse `api-ip:{ip}`.

- `CACHE_STORE=array` **kullanılmaz** (process bazlı, limit çalışmaz).
- Tek sunucuda `file` veya `database`, çok sunucuda **Redis zorunlu** (aksi halde her node kendi sayacını tutar, gerçek limit N katına çıkar).
- Redis düşerse istekler limitlenmeden geçer; Redis health'i alarm kapsamında olmalı.

Hesaplama cache'i de aynı store'u kullanır (`config/api_platform.php` → `calc_cache_ttl`, `idempotency_window`).

## 3. Kullanım logu budama

`api_usage_logs` her istekte bir satır yazar; büyümesi hızlıdır.

```bash
php artisan api:prune-usage-logs --months=12            # 12 aydan eskiyi sil
php artisan api:prune-usage-logs --months=12 --dry-run  # sadece say
php artisan api:prune-usage-logs --months=6 --chunk=2000 --sleep=100
```

Silme, tabloyu kilitlememek için chunk'lar halinde (varsayılan 1000 satır) yapılır.
Zamanlama `routes/console.php` içinde: her ayın 1'i 03:30, `withoutOverlapping`.

Scheduler'ın sunucuda çalıştığını doğrula:

```bash
php artisan schedule:list
crontab -l | grep "schedule:run"
```

## 4. PayTR sandbox testi

API planı ve kredi paketi satın alımları normal sepet/PayTR akışını kullanır (`PaytrService`).

1. `.env`: `PAYTR_MERCHANT_ID`, `PAYTR_MERCHANT_KEY`, `PAYTR_MERCHANT_SALT` tanımlı olmalı.
2. `APP_ENV` production **değilse** `test_mode=1` otomatik gönderilir — sandbox için ek ayar gerekmez.
3. Bildirim URL'i PayTR panelinde `https://<domain>/odeme/paytr/bildirim` olarak tanımlı olmalı
   (bu path CSRF istisnasındadır, bkz. `bootstrap/app.php`).
4. Test akışı: `/api-servisleri` → plan veya kredi paketi sepete ekle → ödeme → PayTR test kartı ile onayla.
5. Doğrulama:
   - `user_api_subscriptions` tablosunda `status=active` satır (plan alımı), veya
   - `users.api_credit_balance` artışı + `api_credit_transactions` içinde `type=topup` satırı (kredi paketi).
6. Ödeme dönmezse: `storage/logs/laravel.log` içinde PayTR bildirim logları; sandbox'ta yerel sertifika
   sorunu varsa `PAYTR_VERIFY_SSL=false` (yalnızca geliştirme).

## 5. MCP endpoint

| | |
|---|---|
| URL | `POST /mcp` (JSON-RPC 2.0) |
| Tanım | `routes/ai.php` → `Mcp::web('/mcp', AstroBilgeServer::class)` |
| Auth | `X-Api-Key: ab_live_...` (REST ile aynı key) |
| Limit | `throttle:api-platform` — plan bazlı |
| GET/DELETE | 405 (spec gereği) |

Duman testi:

```bash
curl -X POST https://<domain>/mcp \
  -H "X-Api-Key: ab_test_..." -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

MCP çağrıları `api_usage_logs.source = 'mcp'` olarak kaydedilir; admin kullanım panosunda REST'ten ayrı seri olarak görünür.

## 6. Admin ekranları

| Ekran | Route |
|-------|-------|
| Planlar | `admin.api.plans.index` |
| Kredi paketleri | `admin.api.credit-packs.index` |
| API key'ler (askıya alma) | `admin.api.keys.index` |
| Kullanım panosu + manuel kredi düzeltme | `admin.api.usage.index` |

Manuel kredi düzeltmesi **yalnızca** panodaki form üzerinden yapılır; `users.api_credit_balance`
doğrudan UPDATE edilmez — aksi halde `api_credit_transactions` defteri bakiye ile tutarsız kalır.

## 7. Olay müdahale kısa listesi

| Belirti | İlk bakılacak |
|---------|---------------|
| Tüm chart endpoint'leri 500 | swetest binary / `storage/app/sweph` izinleri |
| 429 yağmuru | Cache store ayakta mı, plan `rate_limit_per_minute` değeri |
| Kredi düşmüyor / iki kez düşüyor | `api_credit_transactions` defteri + `api_usage_logs.request_hash` (idempotency penceresi) |
| Bir key kötüye kullanılıyor | Admin → API Key'ler → askıya al (anında etkili) |
| DB diski şişiyor | `api:prune-usage-logs` çalışıyor mu (`schedule:list`) |

## 6. AI yorum uçları — uzun üretim, 202 + report_id akışı (2026-08-18)

`POST /api/v1/interpretation/{natal|relationship|annual}` senkron çalışır ve 20-25 sayfalık rapor için
OpenAI çağrısı **dakikalar** sürer. Müşteri istemcisi (ya da aradaki bir proxy) daha erken zaman
aşımına uğrarsa "istek geliyor ama yorum gelmiyor" görünür. Davranış:

| Durum | Yanıt | Kredi |
|-------|-------|-------|
| Rapor cache'te | 200, `data.status=ready`, `from_cache=true` | 0 |
| İlk üretim, tamamlandı | 200, `data.status=ready`, `data.report_id` | 1 kez |
| Aynı rapor başka istekte üretiliyor | **202**, `data.status=processing`, `poll_url`, `Retry-After: 15` | 0 (`api_usage_logs.status=pending`) |
| `GET /api/v1/interpretation/{type}/{report_id}` | 200 ready / 202 processing / 404 `REPORT_NOT_FOUND` | 0 (SKU `interpretation.status`, ücretsiz) |

- Üretim isteği `ignore_user_abort(true)` ile çalışır: istemci bağlantıyı koparsa bile rapor sonuna kadar
  üretilip `ai_interpretations`'a yazılır; müşteri aynı gövdeyle tekrar POST attığında cache'ten alır.
- Aynı hash için `Cache::lock('apiv1:interpretation:lock:{type}:{hash}', 900)` + salt-okunur
  `apiv1:interpretation:generating:{type}:{hash}` işareti tutulur → eşzamanlı ikinci üretim ve ikinci
  ücretlendirme olmaz. İkisi de üretim bitince (başarı/hata) temizlenir; süreç dışarıdan öldürülürse en
  geç 15 dk sonra serbest kalır. Cache store `file`/`database`/`redis` olmalı (`array` OLMAZ).
  **Deploy sırasında `php artisan cache:clear` kilitleri de siler** — o anda üretimde olan bir rapor için
  ikinci bir üretim başlayabilir; yoğun saatte deploy etmekten kaçının.
- `GET .../{report_id}` yalnızca o raporu başlatmış/yoklamış müşteriye açılır (`api_usage_logs.request_hash`
  + `api_customer_id` eşleşmesi); başka müşteri aynı id ile 404 alır.
- `language=en` artık modele "İngilizce yaz" talimatı olarak gider (önceden yok sayılıyordu).

**Deploy sonrası (Windows/IIS):**
1. `php artisan migrate --force` (gitpull-reload.bat migrate ÇALIŞTIRMAZ) — çeviri seed'i +
   `2026_08_18_120000_widen_ai_interpretations_content_column` (TEXT→LONGTEXT). Kontrol:
   `SHOW COLUMNS FROM ai_interpretations LIKE 'content'` → `longtext` olmalı.
2. `php artisan optimize:clear && php artisan route:cache && php artisan config:cache`.
3. IIS FastCGI zaman aşımı, PHP sürecini raporun ortasında öldürmemeli: `activityTimeout` ve
   `requestTimeout` ≥ 600 sn (IIS Manager → FastCGI Settings → php-cgi.exe). Aksi hâlde ne cache
   ne kredi düşümü olur, `GET .../{report_id}` sürekli 404 döner.

**Prod'da teşhis (bir müşteri şikayetinde bakılacaklar):**
- Admin → API Platformu → anahtar detayı → kullanım tablosu: ilgili `interpretation.*` satırlarının
  `status` (success/cached/pending/error/rejected), `http_status`, `duration_ms`.
  `success` + uzun `duration_ms` = rapor üretildi ama istemci beklemedi (istemci zaman aşımı).
- `storage/logs/laravel.log`: `OpenAI Long Report - İstek gönderiliyor` → `HTTP yanıt alındı`
  (`request_duration_ms`) → `Başarılı`. "İstek gönderiliyor"dan sonra hiç satır yoksa PHP süreci
  dışarıdan (IIS) kesilmiştir. `OpenAI Long Report - Exception` + `is_timeout=true` ise
  `OpenAIServiceRefactored::sendLongReport` içindeki 180 sn'lik OpenAI zaman aşımı yetmiyordur.
