💳 PayTR Global Ödeme Sistemi

Polymorphic İlişki ile Merkezi Ödeme Altyapısı - Kurulum Rehberi

🎯 1. NE YAPIYORUZ?
🏗️ 2. MİMARİ
📋 3. ADIMLAR
💻 4. KOD ÖRNEKLERİ
🔐 5. PAYTR API

Hedef: Global Ödeme Sistemi

Şu an Shop modülü var ve sadece ürün satışları için ödeme alabiliyorsun.

YENİ SİSTEM:
Tek ödeme altyapısı ile herhangi bir modülden ödeme alabileceksin:

🛒 Shop Modülü

  • Ürün satışları
  • Sepet ödemeleri
  • Taksitli alışveriş

👤 Membership Modülü

  • Üyelik ödemeleri
  • Abonelik sistemi
  • Aylık/yıllık paketler

📅 Booking Modülü

  • Rezervasyon ödemeleri
  • Kapora sistemi
  • Otel, araç kiralama

💰 Diğer Modüller

  • Bağış sistemi
  • Fatura ödemeleri
  • Gelecekteki modüller

🎉 EN BÜYÜK AVANTAJ:

Tek bir yerde tüm ödemeler!

PayTR, Stripe, Iyzico gibi gateway'leri eklemek çok kolay.
Yeni modül eklersen, ödeme altyapısı hazır!

🔍 Polymorphic İlişki Nedir?

Basit Türkçe:

ESKI YÖNTEM Her modül kendi ödeme tablosunu yapıyor:
• shop_payments
• membership_payments
• booking_payments
❌ Aynı kod 3 kere yazılıyor!

YENİ YÖNTEM Tek tablo, tüm modüller kullanıyor:
payments (payable_type, payable_id)
✅ Kod bir kere, her yerde kullan!

payments tablosu → "Ben ShopOrder'dan mı, Subscription'dan mı geliyorum?" biliyor!

🏗️ Yeni Klasör Yapısı

Modules/Payment/ ← YENİ MODÜL (global) ├── app/ │ ├── Models/ │ │ ├── Payment.php ← Ana ödeme kaydı (polymorphic) │ │ └── PaymentMethod.php ← PayTR, Stripe, Iyzico tanımları │ │ │ ├── Services/ │ │ ├── PaymentService.php ← Ana servis (her modül bunu kullanır) │ │ └── Gateways/ │ │ ├── PaymentGatewayInterface.php ← Contract │ │ ├── PayTRGateway.php ← PayTR implementasyonu │ │ └── StripeGateway.php ← Stripe (gelecekte) │ │ │ └── Contracts/ │ └── Payable.php ← ShopOrder, Subscription implement eder │ └── database/ └── migrations/ ├── 001_create_payment_methods_table.php └── 002_create_payments_table.php ← payable_type, payable_id

🗄️ Veritabanı Tabloları

1. payment_methods (Ödeme Yöntemleri)

Kolon Açıklama Örnek
payment_method_id ID 1, 2, 3...
gateway Hangi gateway? paytr, stripe, iyzico
title Başlık (JSON çoklu dil) {"tr":"Kredi Kartı","en":"Credit Card"}
gateway_config API keys (JSON) {merchant_id, merchant_key, merchant_salt}
supports_installment Taksit var mı? true / false

2. payments (Tüm Ödemeler - Polymorphic)

Kolon Açıklama Örnek
payment_id ID 1, 2, 3...
payable_type Hangi model? Modules\Shop\App\Models\ShopOrder
payable_id Hangi kayıt? 123 (ShopOrder ID: 123)
amount Tutar 150.00 TRY
status Durum pending, completed, failed
gateway Hangi gateway kullanıldı? paytr
gateway_transaction_id PayTR merchant_oid ORD-2024-001

💡 Polymorphic Nasıl Çalışıyor?

// ÖRNEK 1: Shop Order Ödeme payable_type: "Modules\Shop\App\Models\ShopOrder" payable_id: 123 → ShopOrder ID: 123 için ödeme // ÖRNEK 2: Membership Ödeme payable_type: "Modules\Membership\App\Models\Subscription" payable_id: 45 → Subscription ID: 45 için ödeme // ÖRNEK 3: Booking Ödeme payable_type: "Modules\Booking\App\Models\Reservation" payable_id: 67 → Reservation ID: 67 için ödeme

📋 Entegrasyon Adımları (10 Adım)

Her adımı işaretleyerek ilerle:

Adım 1: Analiz ve Hazırlık

TAMAMLANDI

  • ✅ Shop modülü incelendi
  • ✅ Mevcut ödeme altyapısı kontrol edildi
  • ✅ Global mimari tasarlandı
  • ✅ Dokümantasyon hazırlandı
1

Adım 2: Payment Modülünü Oluştur

# Modül oluştur php artisan module:make Payment # Klasör yapısını kontrol et ls -la Modules/Payment/
2

Adım 3: Migration'ları Oluştur

# Central migration php artisan make:migration create_payment_methods_table --path=Modules/Payment/database/migrations php artisan make:migration create_payments_table --path=Modules/Payment/database/migrations # Tenant migration (kopyala) cp Modules/Payment/database/migrations/*.php Modules/Payment/database/migrations/tenant/
⚠️ ÖNEMLİ: Her migration hem central hem tenant klasöründe olmalı!
3

Adım 4: Model'leri Oluştur

# Payment model php artisan make:model Payment --path=Modules/Payment/app/Models # PaymentMethod model php artisan make:model PaymentMethod --path=Modules/Payment/app/Models
4

Adım 5: PayTRService Oluştur

Modules/Payment/app/Services/Gateways/PayTRGateway.php dosyasını oluştur.
KOD ÖRNEĞİ Tab 4'te var!

5

Adım 6: PaymentController Oluştur

php artisan make:controller PaymentController --path=Modules/Payment/app/Http/Controllers
6

Adım 7: Route'ları Ekle

routes/web.php dosyasına ekle:

Route::middleware(['tenant'])->prefix('payment')->group(function () { Route::get('/frame/{payment_number}', [PaymentController::class, 'frame']) ->name('payment.frame'); Route::post('/callback', [PaymentController::class, 'callback']) ->name('payment.callback'); Route::get('/success', [PaymentController::class, 'success']) ->name('payment.success'); Route::get('/failed', [PaymentController::class, 'failed']) ->name('payment.failed'); });
🚨 CSRF EXEMPT: app/Http/Middleware/VerifyCsrfToken.php
payment/callback route'unu CSRF'den muaf tut!
7

Adım 8: ShopOrder'a Payable Ekle

Modules/Shop/app/Models/ShopOrder.php güncelle:

use Modules\Payment\App\Contracts\Payable; class ShopOrder extends Model implements Payable { public function payments() { return $this->morphMany(Payment::class, 'payable'); } public function getPaymentAmount(): int { return (int) ($this->total_amount * 100); // Kuruş } // Diğer interface metodları... }
8

Adım 9: .env Ayarları

# PayTR Test Credentials PAYTR_MERCHANT_ID=test_xxxxx PAYTR_MERCHANT_KEY=test_xxxxx PAYTR_MERCHANT_SALT=test_xxxxx PAYTR_MODE=test
9

Adım 10: Test Et!

TEST KARTI

Kart No: 4355 0843 5508 4358
Tarih: 12/26
CVV: 000

🎉 Tamamlandığında:

✅ Shop siparişleri PayTR ile ödeme alabilecek
✅ Aynı altyapı ile Membership, Booking eklenebilecek
✅ Yeni gateway (Stripe, Iyzico) eklemek kolay olacak

💻 Hazır Kod Örnekleri

Kopyala-yapıştır ile kullanabilirsin:

1. Payable Interface

Modules/Payment/app/Contracts/Payable.php

2. ShopOrder → Payable Implementation

Modules/Shop/app/Models/ShopOrder.php

use Modules\Payment\App\Contracts\Payable; class ShopOrder extends Model implements Payable { public function payments() { return $this->morphMany(Payment::class, 'payable'); } public function getPaymentAmount(): int { return (int) ($this->total_amount * 100); // TRY → Kuruş } public function getPaymentCurrency(): string { return 'TRY'; } public function getPaymentDescription(): string { return "Sipariş: {$this->order_number}"; } public function getPaymentCustomer(): array { return [ 'name' => $this->customer_name, 'email' => $this->customer_email, 'phone' => $this->customer_phone, 'address' => $this->shipping_address, ]; } public function getPaymentBasket(): array { $basket = []; foreach ($this->items as $item) { $basket[] = [ $item->product_title, number_format($item->unit_price, 2, '.', ''), $item->quantity, ]; } return $basket; } public function onPaymentCompleted(Payment $payment): void { $this->update([ 'payment_status' => 'paid', 'status' => 'confirmed', ]); // Sepeti temizle app(ShopCartService::class)->clearCart(); } public function onPaymentFailed(Payment $payment): void { $this->update(['payment_status' => 'failed']); } }

3. Checkout'ta Kullanım

CheckoutPageNew.php submitOrder() metodunda:

public function submitOrder() { // ... order oluştur ... $paymentService = app(PaymentService::class); $paymentMethod = PaymentMethod::where('gateway', 'paytr')->first(); $result = $paymentService->initiatePayment($order, $paymentMethod, [ 'payment_type' => 'purchase', 'no_installment' => 0, 'max_installment' => 12, ]); if ($result['success']) { return redirect($result['redirect_url']); // PayTR iframe } else { session()->flash('error', $result['error']); } }

📁 Tam Kod Örnekleri

Detaylı kod örnekleri için:

Tüm Kod Şablonlarını Gör (28 KB)

🔐 PayTR API - Önemli Detaylar

PayTR entegrasyonunda mutlaka bilmen gerekenler:

1. Hash Hesaplama (Token İsteği)

$hash_str = $merchant_id . $user_ip . $merchant_oid . $email . $payment_amount . // ⚠️ KURUŞ CİNSİNDEN! $user_basket . $no_installment . $max_installment . $currency . $test_mode . $merchant_salt; // ⚠️ EN SONDA! $paytr_token = base64_encode( hash_hmac('sha256', $hash_str, $merchant_key, true) ); // ⚠️ raw_output = true

🚨 KRİTİK NOKTALAR:

  • payment_amount KURUŞ cinsinden (100.00 TRY = 10000)
  • merchant_salt en sonda eklenir
  • merchant_key HMAC key olarak kullanılır
  • raw_output = true parametresi zorunlu
  • Sonuç base64_encode ile encode edilir

2. user_basket Formatı

// Sepet bilgisi (JSON Array) $basket = [ ["Transpalet Forklift", "1500.00", 1], ["Kargo Ücreti", "50.00", 1] ]; // JSON encode → base64 encode $user_basket = base64_encode(json_encode($basket));

UYARI Birim fiyat string olmalı!

3. Callback Hash Doğrulama

// PayTR'den gelen POST verileri $merchant_oid = $_POST['merchant_oid']; $status = $_POST['status']; // "success" veya "failed" $total_amount = $_POST['total_amount']; // Kuruş cinsinden $hash = $_POST['hash']; // Hash hesapla $hash_str = $merchant_oid . $merchant_salt . $status . $total_amount; $calculated_hash = base64_encode( hash_hmac('sha256', $hash_str, $merchant_key, true) ); // Doğrula if ($calculated_hash !== $hash) { echo "FAILED: Invalid hash"; exit; } // ✅ Hash doğru, işleme devam et echo "OK"; // ⚠️ MUTLAKA "OK" DÖNDÜR!

🚨 CALLBACK KURALLARI:

  • Hash mutlaka doğrulanmalı
  • Response "OK" olmalı (yoksa PayTR tekrar gönderir)
  • Duplicate callback handle edilmeli
  • 200 OK HTTP status dönülmeli
  • Max 30 saniye timeout

4. Test Kartları

Durum Kart No Tarih CVV
✅ Başarılı 4355 0843 5508 4358 12/26 000
❌ Başarısız 5406 6754 0667 5403 12/26 000

5. Hata Kodları (Sık Karşılaşılanlar)

Kod Açıklama Çözüm
2 Geçersiz token Hash yanlış hesaplanmış
10 Geçersiz tutar payment_amount kuruş cinsinden olmalı
20 Kart reddedildi Müşteri bankasıyla iletişime geçmeli
21 Yetersiz bakiye Başka kart denenmeli
22 3D Secure başarısız SMS kodu yanlış girilmiş

📚 Detaylı API Dökümanı

Tüm API detayları, parametreler, örnekler için:

PayTR API Referansını Gör (16 KB)