voxyfy / anadolupay
Türk banka sanal POS'ları ve ödeme kuruluşları için birleşik Laravel ödeme geçidi — NestPay, Garanti, PosNet, PayFlex, PayFor, InterPos, KuveytPos, PayTR, Param.
Requires
- php: ^8.2
- illuminate/http: ^12.0|^13.0
- illuminate/support: ^12.0|^13.0
- psr/log: ^2.0|^3.0
Requires (Dev)
- larastan/larastan: ^3.10
- laravel/pint: ^1.30
- nunomaduro/collision: ^8.8
- orchestra/testbench: ^10.0|^11.0
- pestphp/pest: ^3.0|^4.0|^5.0
- pestphp/pest-plugin-arch: ^3.0|^4.0|^5.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0|^5.0
README
AnadoluPay
Türk bankalarının sanal POS'ları için tek arayüz.
Türkiye'de banka entegrasyonu yazmak, aynı işi on yedi kez farklı şekilde
yapmaktır. Garanti tutarı kuruş cinsinden tam sayı ister, NestPay ondalıklı
dizgi. Garanti hash'i ayraçsız birleştirip büyük harfe çevirir, NestPay alanları
sıralayıp | ile birleştirir, PosNet ; kullanır. Kuveyt Türk size form
alanları değil hazır bir HTML sayfası döner. VakıfBank provizyon adımında kart
bilgisini bir daha ister. Bunların hiçbiri dokümantasyonda yan yana yazmaz;
her birini ayrı ayrı öğrenirsiniz.
Bu paket o farkları tek bir sözleşmenin arkasına alır:
$response = AnadoluPay::driver('garanti')->createPayment($data); return response($response->toHtmlForm());
Bankayı değiştirmek için 'garanti' yerine 'akbank' yazmak yeterlidir.
Çalışan bir örnek uygulama için: Voxyfy/anadolupay-laravel
Laravel'e değil düz Node.js/TypeScript'e bağlı kalmak isteyenler için aynı protokol bilgisiyle taşınan bir port da var: Voxyfy/anadolupay-node (npm) — test etmek için Voxyfy/anadolupay-node-example.
Aynı "çok sağlayıcı, tek arayüz" mimarisini kargo firmaları için uygulayan bir kardeş proje de var: Voxyfy/anadoluship (npm) — MNG, UPS, Yurtiçi, Aras, PTT ve Sürat Kargo için Node.js/TypeScript SDK'sı.
LLM API'lerine (OpenAI/Anthropic/Gemini) müşteri verisi gönderirken KVKK riskini azaltmak için de bir kardeş kütüphane var: Voxyfy/anadolushield (npm) — TCKN, VKN, IBAN, telefon, isim gibi kişisel verileri LLM'e göndermeden önce maskeler.
KVKK/GDPR uyumlu çerez rızası için de bir kardeş kütüphane var: Voxyfy/anadolucookie — framework'e bağımlı olmayan çerez rıza (cookie consent) banner kütüphanesi.
Ne yapmaz: Arayüz üretmez, sipariş durumu tutmaz, stok düşmez, fatura kesmez. Ödeme akışını yürütür ve yanıtı normalleştirir; gerisi sizin uygulamanızın işi.
Canlıya çıkmadan önce okuyun
Bu paketteki protokoller bankaların public dokümantasyonuna göre yazıldı ve istek üretimi, imza ve yanıt eşlemesi birim testleriyle kilitlendi. Ancak banka driver'larının çoğu gerçek bir bankaya karşı çalıştırılmadı. (Ölçülmüş olanlar için Doğrulama durumu tablosuna bakın — hangisinin nereye kadar götürüldüğü orada yazılıdır.)
Testler benim yazdığım algoritmayı doğrular, bankanın beklediğini değil. Bir alanın sırası yanlışsa test yeşil kalır, banka işlemi reddeder. Kullanacağınız her banka için kendi test üye işyeri bilgilerinizle en az bir 3D Secure satış ve bir iade çalıştırın. Hash hesabında bankalar zaman zaman kuruluma özel farklılıklar tanımlıyor.
Bu uyarı banka driver'ları içindir.
iyzicodriver'ı bunun dışındadır: sandbox ortamında uçtan uca çalıştırılıp doğrulanmıştır — bkz. Doğrulama durumu.
Doğrulama durumu
Her driver aynı ölçüde doğrulanmış değil. Bu tablo hangisinin nereye kadar ölçüldüğünü gösterir; "test var" ile "gerçekten çalışıyor" aynı şey değildir.
| Seviye | Ne anlama gelir |
|---|---|
| Uçtan uca | Sağlayıcının sandbox'ında gerçek bir ödeme tamamlandı |
| Test vektörü | İmza, sağlayıcının kendi yayınladığı vektörle birebir eşleşiyor |
| Dokümana göre | Protokol dokümandan uygulandı, sağlayıcıya karşı ölçülmedi |
| Driver | Seviye | Dayanak |
|---|---|---|
iyzico |
Uçtan uca | 2026-08-09, sandbox 3D Secure satış |
kuveytturk |
Uçtan uca (ödeme) | 2026-08-09 — 3D doğrulama ve provizyon (OTORİZASYON VERİLDİ); sorgu/iade doğrulanmadı |
craftgate |
Test vektörü | Resmî istemci depolarındaki üç vektör |
moka |
Uçtan uca | 2026-08-09, test servisinde 3D Secure satış tamamlandı |
tosla |
Uçtan uca | 2026-08-09 — 3D satış, durum, taksit, iade ve iptal doğrulandı |
paratika |
Dokümana göre | Vektör yayınlanmıyor |
| NestPay bankaları | Uçtan uca | 2026-08-09, Ziraat test terminali — 3D satış, durum, iade ve iptal. ziraat, akbank ve isbank ayrıca kendi mağazalarında ölçüldü; kalan altı banka yalnızca bu ortak ölçüme dayanır |
halkbank, teb, sekerbank, ing, alternatifbank (NestPay) |
Dokümana göre | Uçlar canlı ve ayakta (entegrasyon.asseco-see.com.tr ortak ucu ve kendi sanalpos.* adresleri 200 dönüyor; torus-stage-halkbank 503, torus-stage-alternatifbank DNS'te yok, diğerleri stage'de de ayakta). Ancak bu beş banka için hiçbir yerde (bankanın kendi sitesi, Payten dokümanları, mewebstudio/pos gibi topluluk paketleri) açık test clientid/storekey/API kullanıcısı yayınlanmıyor — isbank ve turkiyefinans'ın aksine. Ölçüm için bankadan veya Payten'den (destek@payten.com) test üye işyeri istenmesi gerekiyor; kör tahminle deneme yapılmadı, çünkü yanlış kimlik de doğru kimlik gibi mdStatus hatası üretebiliyor ve bu ayrım ölçmeden yapılamıyor |
isbank (NestPay) |
Uçtan uca | 2026-08-11, İş Bankası'nın kendi mağazası (entegrasyon.asseco-see.com.tr, mağaza 700655000200) — 3D Secure tam turu (3DS 2.2.0, mdStatus 1, provizyon Approved, sorgu paid, iade Approved) örnek projeden tamamlandı; ayrıca 3D'siz satış, taksitli satış, iptal, iade, kısmi iade, ön provizyon, kapama ve sorgu. Dolaşımdaki yapılandırmaların ISBANK07'yi storekey sanması yüzünden bu driver uzun süre mdStatus 7 veriyordu — ISBANK07 API şifresidir, storekey TRPS0200 |
turkiyefinans (NestPay) |
Kısmen ölçüldü | 2026-08-11, bankanın kendi yayınladığı NestPay test dokümanındaki mağaza (280000100) — 3D formu bankaca kabul edilip 3DS akışına girdi ve storekey TRPS2828 doğrulandı (yanlış anahtar mdStatus 7 veriyor). Provizyon, iade, iptal ve sorgu ölçülemedi: API kullanıcısı TFKBAPI bu mağazada yetkili değil, her istek 99 / Insufficent permissions dönüyor. Dokümandaki ikinci mağaza 280000200 artık yok (mdStatus 6) |
qnb (NestPay) |
Kısmen ölçüldü | 2026-08-11, Asseco/Finansbank'ın resmî "NestPay Test Dokümanı"ndaki paylaşılan mağaza (600100000) — 3D Secure tam turu (3DS 2.2.0, mdStatus 1, imza ve CAVV doğrulandı) örnek projeden iki farklı test kartıyla tamamlandı. Provizyon ikisinde de aynı jenerik hatayla düştü: ProcReturnCode 99, ERRORCODE ISO8583-19 ("Tekrar girin, tekrar deneyin"). Kart değişkeni elendiği için Türkiye Finans'takiyle aynı kalıp: paylaşılan kimlik 3D'yi geçirir ama provizyona yetkili değil. Gerçek ortam (www.fbwebpos.com) artık DNS'te çözülmüyor; test ortamı ortak Asseco ucunda (entegrasyon.asseco-see.com.tr) çalışıyor |
akbank (NestPay) |
Uçtan uca | 2026-08-11, Akbank'ın kendi test mağazası (entegrasyon.asseco-see.com.tr, mağaza 100100000) — 3D Secure tam turu (3DS 2.2.0, mdStatus 1, provizyon Approved) örnek projeden tamamlandı; ayrıca 3D'siz satış, taksitli satış, iptal, iade, kısmi iade, ön provizyon, kapama ve durum sorgusu. Storekey tahmin edilmedi, yanlış değerlerin mdStatus 7 vermesiyle ölçülerek ayrıldı |
garanti (GVPS) |
Uçtan uca | 2026-08-11, banka test terminali — 3D Secure tam turu (mdStatus 1, dönüş hash'i doğrulandı, provizyon 00 Approved) örnek projeden tamamlandı; ayrıca 3D'siz satış (sekiz test kartının yedisi), taksitli satış, sipariş sorgusu ve hareket dökümü. İade, iptal ve ön provizyon ölçülemedi: terminal bunları 05 / RPC-05 ile reddediyor. Driver kusuru değil — istek biçiminin on dört çeşitlemesi aynı yanıtı verdi, PROVAUT ile denendiğinde 92 / 0652 yetkiniz yok gelmesi kimliğin doğru kabul edildiğini gösteriyor |
vakifbank (PayFlex) |
Uçtan uca | 2026-08-10, banka sandbox'ı — 3D Secure satış tamamlandı; ayrıca non-3D satış, durum, iade (kısmî ve tam), iptal, ön provizyon ve kapama |
akbank-pos |
Uçtan uca | 2026-08-10, Akbank test store — 3D Secure satış ve iptali örnek projeden tamamlandı; ayrıca non-3D satış, kısmî/tam iade, ön provizyon, kapama ve işlem geçmişi. Dönüş imzası bankanın ürettiği hash ile birebir eşleşti |
qnb-payfor (PayFor) |
Uçtan uca | 2026-08-10, QNB demo ortamı — 3D Secure satış (Onaylandı), durum sorgusu ve iptal; dönüş imzası bankanın ürettiği ResponseHash ile iki ayrı gerçek dönüşte birebir eşleşti |
ziraat-payflex |
Kısmen ölçüldü | 2026-08-10, Innova preprod — 3D başlatma (MPI Enrollment) bankanın kendi test ortamında geçti; banka üye işyeri ve şifreyi kabul edip tam VERes döndürdü (PaReq, ACSUrl, TermUrl, MD). Satış/iade/iptal ölçülemedi: VPOS ucu ayrı bir iş yeri şifresi ve TerminalNo istiyor (5001). 3D'ye kayıtlı test kartı da bulunamadı — denenen altı kart Status N veya kart hatası verdi |
paycell |
Kısmen ölçüldü | 2026-08-10, Turkcell test ortamı — kart token alındı ve token yanıtının imzası sağlayıcının ürettiğiyle birebir eşleşti (test vektörü olarak kilitlendi); 3D oturumu da gerçek ortamda açıldı. Ödeme adımı ölçülemedi: yayınlanmış ortak test üye işyeri (9998) kart provizyonunda 4000 Bank error döndürüyor |
yapikredi (PosNet) |
Kısmen ölçüldü | 2026-08-10, setmpos.ykb.com — şifreleme, 3D form, bankanın ACS sayfası ve oosResolveMerchantData gerçek ortamda geçildi; dönüş MAC'i bankanın ürettiğiyle birebir eşleşti ve test vektörü olarak kilitlendi. Finansallaştırma ölçülemedi: banka IP tanımlaması istiyor (0148 UNAUTHORIZED REQUEST) ve yayınlanmış test kartlarının hepsi eskimiş |
denizbank (InterPos) |
Dokümana göre | 3D form imzası bağımsız bir implementasyonla birebir aynı; ölçüm yapılamadı — test ortamı IP tanımlaması istiyor ve test üye işyeri bilgileri yayınlanmıyor |
tami |
Dokümana göre — çelişkili | dev.tami.com.tr dokümantasyonundan uygulandı, sandbox kimliği henüz yok. securityHash formülü için doküman kendi içinde çelişkili (bkz. Tami); iade/iptal aynı uca gidiyor, ön provizyon kapama ucu tahmin |
| Diğer banka driver'ları | Dokümana göre | Banka entegrasyon dokümanları |
iyzico'da tam olarak ne doğrulandı
2026-08-09'da iyzico sandbox'ında, örnek projeden, tarayıcıyla tamamlanan bir 3D Secure satış (100,00 TL, tek çekim, resmî test kartı) şunları kanıtladı:
Authorizationbaşlığı (IYZWSv2) — iyzico isteği kabul etti,paymentIdverdi- Initialize yanıt imzası — doğrulama açıkken geçti
- 3DS dönüş imzası — gerçek dönüş,
conversationData:conversationId:mdStatus:paymentId:statussırasıyla birebir eşleşti - Provizyon (auth) yanıt imzası —
/payment/3dsecure/authyanıtı doğrulandı,authCodealındı
Aynı oturumda şu işlemler de sandbox'a karşı çalıştırıldı:
| İşlem | Sonuç |
|---|---|
| Durum sorgusu | paid, 100,00 TL, maskeli kart — sipariş numarasıyla |
| Durum sorgusu (bulunamayan kayıt) | found: false / unknown — sessizce "ödendi" demiyor |
| BIN sorgusu | Akbank · master_card · debit |
| Taksit sorgusu | Debit kartta tek seçenek, kredi kartta 1/2/3/6/9/12 — taksit kart tipine ve işyeri anlaşmasına bağlı |
| İade (tutarsız) | Kusur bulundu ve düzeltildi: price gönderilmiyordu, iyzico 5004 ile reddediyordu |
| İade (düzeltme sonrası) | Başarılı — iade yanıt imzası da doğrulandı; iyzico işlemi transactionType: CANCEL olarak kaydetti |
Bunlar doğrulanmadı: diğer ödeme modelleri (3d_pay, 3d_host,
regular) ve canlı ortam. Sandbox'ta çalışan bir akış canlıda da çalışır diye
bir garanti yoktur; üye işyeri tanımınız farklı olabilir.
iyzico'nun ayrı bir iptal işlemi yoktur — bu yüzden driver
SupportsCancellation arayüzünü uygulamaz. Aynı gün yapılan tam iadeyi iyzico
kendisi iptal olarak işler; yukarıdaki testte yanıt transactionType: CANCEL
döndü. Yani iptal etmek için refund() çağırmanız yeterli.
İçindekiler Kurulum · Doğrulama durumu · Örnek proje · Desteklenen bankalar · Nasıl çalışır · Yapılandırma · Ödeme akışı · İade ve iptal · Ödeme modelleri · Yetenekler · Tutarlar · Bankaların tuhaflıkları · Hata yönetimi · Event'ler · Loglama · Test ortamı · Test kartları · Güvenlik · Yeni banka eklemek · Yol haritası
Kurulum
composer require voxyfy/anadolupay
php artisan vendor:publish --tag="anadolupay-config"
PHP 8.2+, Laravel 12 veya 13. Auto-discovery açıktır, ek adım yoktur.
Laravel 13 en az PHP 8.3 ister; PHP 8.2 kullanıyorsanız Laravel 12'de kalırsınız. CI her iki kombinasyonu da koşar.
Uçtan uca kurulmuş bir Laravel projesi görmek isterseniz anadolupay-laravel deposu ödeme başlatma, 3D dönüşü ve iade akışlarını örnekliyor.
Desteklenen bankalar
Türkiye'deki sanal POS'lar birkaç ortak altyapı ailesine iner. Aynı aileyi kullanan bankalar aynı driver'ı paylaşır; aralarındaki fark yalnızca uç nokta ve kimlik bilgisidir.
Aynı banka birden fazla satırda görünebilir. Bir bankanın iki farklı sanal POS ürünü varsa her biri ayrı bir driver'dır: ayrı protokol, ayrı kod, ayrı doğrulama durumu. Birinin ölçülmüş olması diğeri hakkında hiçbir şey söylemez. Bankanın adının yanındaki parantez hangisi olduğunu belirtir — örneğin
akbank(NestPay) ileakbank-pos(yeni JSON API) akraba bile değildir. Ayrıcaakbank-posaşağıdaki ödeme kuruluşları tablosunda yer alır. Hangisinin size tanımlandığını sanal POS sözleşmenizden teyit edin.
Tablodaki Test sütunu, o driver'ın sağlayıcının kendi ortamına karşı koşulup koşulmadığını söyler — geri kalan sütunlar yalnızca protokolün uygulandığını gösterir, çalıştığını değil:
| İşaret | Anlamı |
|---|---|
| ✅ | Sağlayıcının sandbox'ında uçtan uca gerçek bir ödeme tamamlandı |
| ◐ | Kısmen ölçüldü — bazı akışlar doğrulandı, hepsi değil |
| ⏳ | Henüz ölçülmedi; yalnızca dokümana göre uygulandı |
| ⏳ ortak driver | Driver aynı altyapıyı paylaşan başka bir bankanın test ortamında uçtan uca doğrulandı (NestPay ailesi Ziraat'te, PayFlex VakıfBank'ta, PayFor QNB'de). Kod yolu ortaktır, fark yalnızca uç nokta ve kimlik bilgisidir — yine de bu bankanın kendi terminaline karşı ölçülmemiştir |
| ⏳ IP kısıtlı | Ölçüm banka tarafında IP tanımlaması yapılmadan koşulamıyor |
| Driver | Banka | Altyapı | Test | 3D | 3D Pay | 3D Host | Non-secure | İade | İptal |
|---|---|---|---|---|---|---|---|---|---|
akbank |
Akbank (NestPay) | Asseco / Payten | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
isbank |
İş Bankası | Asseco / Payten | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
ziraat |
Ziraat Bankası (NestPay) | Asseco / Payten | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
halkbank |
Halkbank | Asseco / Payten | ⏳ ortak driver | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
qnb |
QNB Finansbank (NestPay) | Asseco / Payten | ◐ 3D geçti, provizyona yetkisiz | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
teb |
TEB | Asseco / Payten | ⏳ ortak driver | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
sekerbank |
Şekerbank | Asseco / Payten | ⏳ ortak driver | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
ing |
ING | Asseco / Payten | ⏳ ortak driver | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
alternatifbank |
Alternatif Bank | Asseco / Payten | ⏳ ortak driver | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
turkiyefinans |
Türkiye Finans | Asseco / Payten | ◐ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
garanti |
Garanti BBVA | GVPS | ✅ | ✅ | ✅ | — | ✅ | ✅ | ✅ |
yapikredi |
Yapı Kredi | PosNet (XML) | ◐ | ✅ | — | — | ✅ | ✅ | ✅ |
albaraka |
Albaraka Türk | PosNet V1 (JSON) | ⏳ | ✅ | — | ✅ | ✅ | ✅ | ✅ |
vakifbank |
VakıfBank | PayFlex V4 | ✅ | ✅ | — | — | ✅ | ✅ | ✅ |
ziraat-payflex |
Ziraat Bankası (PayFlex) | PayFlex V4 | ◐ | ✅ | — | — | ✅ | ✅ | ✅ |
denizbank |
DenizBank | InterPos | ⏳ IP kısıtlı | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
qnb-payfor |
QNB Finansbank / Enpara (PayFor) | PayFor | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
ziraat-katilim |
Ziraat Katılım | PayFor | ⏳ ortak driver | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
kuveytturk |
Kuveyt Türk | BOA / TDV2.0 | ◐ | ✅ | — | — | ✅ | — | — |
vakif-katilim |
Vakıf Katılım | BOA | ⏳ | ✅ | — | ✅ | ✅ | ✅ | ✅ |
Ödeme kuruluşları:
| Driver | Kuruluş | Test | 3D | 3D Pay | 3D Host | Non-secure | İade | İptal |
|---|---|---|---|---|---|---|---|---|
akbank-pos |
Akbank (yeni JSON API) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
paytr |
PayTR | ⏳ | — | ✅ | ✅ | ✅ | ✅ | — |
param |
Param ⚠️ TMSF kayyımlığında, altyapı fiilen kapalı | ⏳ | ✅ | ✅ | — | ✅ | ✅ | — |
tosla |
Tosla (AkÖde) | ✅ | — | ✅ | ✅ | ✅ | ✅ | ✅ |
craftgate |
Craftgate | ⏳ | ✅ | — | — | ✅ | ✅ | — |
moka |
Moka United | ✅ | ✅ | — | — | ✅ | ✅ | ✅ |
paratika |
Paratika (Payten) | ⏳ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
paycell |
Paycell (Turkcell) | ◐ | ✅ | — | — | ✅ | ✅ | ✅ |
iyzico |
iyzico | ✅ | ✅ | — | — | — | — | — |
fake |
geliştirme için sahte driver | — | — | — | — | ✅ | ✅ | — |
Aynı bankanın iki driver'ı varsa ikisi de gerçektir; hangisinin tanımlandığını sanal POS sözleşmenizden teyit edin:
- Akbank →
akbank(eski NestPay) veyaakbank-pos(yeni JSON API) - Ziraat →
ziraat(NestPay) veyaziraat-payflex(PayFlex) - QNB Finansbank →
qnb(NestPay) veyaqnb-payfor(PayFor).qnb'nin varsayılan adresiwww.fbwebpos.comgerçek ortam için artık DNS'te çözülmüyor — ama domain terk edilmiş değil, hâlâ Finansbank'a kayıtlı ve DNSSEC ile aktif imzalanıyor; sadece web kaydı kaldırılmış. Test ortamı ortak Asseco ucunda (entegrasyon.asseco-see.com.tr) çalışıyor: Asseco'nun resmi "Finansbank Test Dokümanı"nda yayınlanmış, bankalar arası paylaşılan bir kimlik var (ClientId600100000, Storekey123456, API kullanıcı/şifreFINANSAPI/FINANS06). 2026-08-11'de örnek projeden ölçüldü: 3D Secure tam turu geçti (mdStatus 1, imza ve CAVV doğrulandı, iki farklı kartla tekrarlandı) ama provizyon ikisinde de aynı jenerik hatayla düştü (ProcReturnCode 99,ERRORCODE ISO8583-19) — Türkiye Finans'takiyle aynı kalıp: paylaşılan kimlik 3D'yi geçirir, provizyona yetkili değil. Kendi üye işyerinizle deneyin;qnb-payforşu an daha az riskli çünkü uçtan uca doğrulanmış.
Nasıl çalışır
Bankalar arasındaki fark yüzeyde protokol (XML / JSON / SOAP / form), derinde imza algoritmasıdır. Paket bu iki katmanı ayırır:
CreatePaymentData ─┐
├─► AnadoluPay::driver('garanti')
CardData ──────────┘ │
▼
┌─────────────────────────────┐
│ AbstractBankGateway │ ortak akış:
│ createPayment / verify │ form → hash → provizyon
│ refund │
└──────────────┬──────────────┘
│ banka-özel eşleme
┌──────────────────────┼──────────────────────┐
▼ ▼ ▼
AssecoGateway GarantiGateway PosNetGateway … (16 driver)
sha512 + '|' sha512 UPPER sha256 + ';'
CC5Request XML GVPSRequest XML posnetRequest XML
│ │ │
└──────────────────────┼──────────────────────┘
▼
BankHttpClient
XML/JSON/form kodlama · maskeli loglama
Bir driver yalnızca yedi metodu doldurur: build3dFormFields(),
checkCallbackHash(), is3dAuthSuccess(), provision(),
mapCallbackResponse(), mapProvisionResponse(), extractOrderId().
Akış kontrolü, hata yönetimi, HTTP ve loglama temel sınıfta tek yerde durur. Bir bankada düzeltilen akış hatası hepsinde düzelir; bu, on yedi kopyanın ayrı ayrı bakımını yapmaktan farkı.
Dönen PaymentResponse ve VerificationResponse bankadan bağımsızdır: hangi
driver'ı kullanırsanız kullanın success, paymentId ve status aynı anlama
gelir. Bankanın ham yanıtı raw içinde korunur — normalleştirme bilgi kaybı
yaratmaz.
Yapılandırma
Yalnızca kullandığınız bankanın değişkenlerini doldurun. Diğer preset'ler boş kalabilir; yalnızca çağrıldıklarında hata verirler.
Aynı kavramın bankalarda farklı adları var:
| Config | Bankadaki karşılığı |
|---|---|
merchant_id |
ClientId · MerchantId · ShopCode · merchantSafeId |
terminal_id |
TerminalId · TerminalNo · terminalSafeId |
username |
Name · UserCode · ProvUserID |
password |
API şifresi |
secret_key |
store key · hash key · GUID (3D anahtarı) |
# Garanti BBVA GARANTI_MERCHANT_ID=xxxxxxx GARANTI_TERMINAL_ID=30690000 GARANTI_USERNAME=PROVAUT GARANTI_PASSWORD=xxxxxxx GARANTI_SECRET_KEY=xxxxxxx GARANTI_REFUND_USERNAME=PROVRFN # iade/iptal ayrı kullanıcı ister GARANTI_REFUND_PASSWORD=xxxxxxx # Yalnızca bayi (alt üye işyeri) yapılandırmalı terminaller için; bankanın # verdiği bayi kodu. Boş bırakılırsa istekler bugünkü hâliyle gider. GARANTI_SUB_MERCHANT_ID= # Banka dokümanı alanı Terminal düğümü içinde tanımlıyor; varsayılanı ancak # bankanız farklı bir düğüm isterse değiştirin. GARANTI_SUB_MERCHANT_ID_PATH=Terminal.SubMerchantID # Akbank (NestPay) AKBANK_MERCHANT_ID=xxxxxxx AKBANK_USERNAME=xxxxxxx AKBANK_PASSWORD=xxxxxxx AKBANK_SECRET_KEY=xxxxxxx # Yapı Kredi PosNet — posnet_id ayrı bir alandır, merchant_id değildir YAPIKREDI_MERCHANT_ID=xxxxxxx YAPIKREDI_TERMINAL_ID=xxxxxxx YAPIKREDI_POSNET_ID=xxxxxxx YAPIKREDI_SECRET_KEY=xxxxxxx
Tüm anahtarlar için yayınladığınız config/anadolupay.php dosyasına bakın.
Sipariş numarası
Sipariş numarasını kendiniz veriyorsanız hiçbir şey değişmez. Vermek
istemiyorsanız orderId'yi boş geçin; paket ön eki yapılandırmadan alıp
numarayı üretir:
ANADOLUPAY_ORDER_PREFIX=ODM- ANADOLUPAY_ORDER_LENGTH=10 # rastgele bölümün uzunluğu, en az 6
new CreatePaymentData(amount: 199.90, currency: 'TRY', orderId: '', ...); // orderId → ODM-4KX9AB2Q7T AnadoluPay::orderId(); // ödemeyi başlatmadan önce gerekirse
Numara A-Z0-9 ile sınırlıdır ve rastgeledir; sayaç tutulmaz. Sebebi: sipariş
numarası bankada kalıcı bir anahtardır — aynı numara ikinci kez gönderilirse
işlem reddedilir ve numara iade/sorgulamada da kullanıldığı için sonradan
değiştirilemez. Sayaç bunun için kalıcı depolama ve kilit gerektirir.
İki sınırı bilerek seçin:
- PosNet (Yapı Kredi, Albaraka) ve Paycell numarayı 20 karaktere sığdırır. Ön ek bu bütçeden düşer; taştığında driver sessizce kesmek yerine hata verir.
- Paycell ön eki tamamen atar. Referans numarasını üretirken rakam dışındaki her karakteri siler, yani benzersizlik tamamen rastgele bölümdedir.
Ödeme akışı
Türk banka sanal POS'larında 3D Secure bir GET yönlendirmesi değil, bankanın 3D geçidine yapılan bir form POST'udur. Akış üç adımdır:
[1] createPayment() [2] tarayıcı [3] verify()
imzalı form üret ──► bankaya POST ──► hash doğrula
kullanıcı SMS/ + provizyon iste
app onayı
1 · Ödemeyi başlat
use Voxyfy\AnadoluPay\DTO\CardData; use Voxyfy\AnadoluPay\DTO\CreatePaymentData; use Voxyfy\AnadoluPay\Facades\AnadoluPay; $data = new CreatePaymentData( amount: 199.90, currency: 'TRY', orderId: 'SIPARIS-123', // boş geçilirse yapılandırmadan üretilir customer: [ 'name' => 'Ahmet Yılmaz', 'email' => 'ahmet@example.com', 'phone' => '5551112233', ], successUrl: route('odeme.donus'), failUrl: route('odeme.donus'), card: new CardData( number: '5528790000000008', expireMonth: '12', expireYear: '2030', cvv: '123', holderName: 'Ahmet Yılmaz', ), installment: 1, paymentModel: CreatePaymentData::MODEL_3D_SECURE, ip: $request->ip(), ); $response = AnadoluPay::driver('garanti')->createPayment($data); return response($response->toHtmlForm());
toHtmlForm() otomatik gönderilen bir sayfa üretir. Formu kendiniz render
etmek isterseniz:
$response->formAction; // bankanın 3D geçidi $response->formMethod; // 'POST' $response->formFields; // imzalı gizli alanlar
Kuveyt Türk, Vakıf Katılım ve Param form alanı yerine hazır bir HTML sayfası
döner; bu durumda formFields boştur ve içerik $response->htmlContent
içindedir. toHtmlForm() iki durumu da doğru ele alır — elle uğraşmak yerine
onu kullanın.
2 · Dönüşü doğrula
use Voxyfy\AnadoluPay\DTO\VerifyPaymentData; $result = AnadoluPay::driver('garanti')->verify(new VerifyPaymentData( payload: $request->all(), headers: $request->headers->all(), rawBody: $request->getContent(), )); if ($result->success) { // $result->paymentId bankanın işlem referansıdır. // Saklayın — iade ve iptal için gerekecek. }
verify() sırayla: dönüş hash'ini doğrular (eşleşmezse
InvalidSignatureException), 3D doğrulama durumunu kontrol eder, klasik 3D
Secure modelinde bankaya provizyon isteğini gönderir. 3D Pay ve 3D Host'ta
provizyon banka tarafında tamamlandığı için ikinci istek atılmaz.
PayFlex (VakıfBank / Ziraat) sipariş bağlamı ister. Banka provizyon adımında kart bilgisini ve tutarı yeniden sorar ama bunları dönüşte göndermez — siz sağlarsınız:
$result = AnadoluPay::driver('vakifbank')->verify(new VerifyPaymentData( payload: $request->all(), order: [ 'id' => 'SIPARIS-123', 'amount' => 199.90, 'currency' => 'TRY', 'ip' => $request->ip(), 'card' => ['number' => '...', 'expire_month' => '12', 'expire_year' => '30', 'cvv' => '123'], ], ));
İade ve iptal
use Voxyfy\AnadoluPay\DTO\RefundPaymentData; AnadoluPay::driver('akbank')->refund(new RefundPaymentData('SIPARIS-123')); // tam AnadoluPay::driver('akbank')->refund(new RefundPaymentData('SIPARIS-123', 49.90)); // kısmi
Gün sonu kapanmadan önce iade değil iptal kullanın — daha hızlı ve komisyonsuzdur:
AnadoluPay::driver('akbank')->cancel(new RefundPaymentData('SIPARIS-123'));
Bazı bankalar işlemi sipariş numarasıyla değil, kendi referanslarıyla eşler.
Bu referansı ödeme sırasında saklayıp metadata ile geçin:
| Banka | Gereken alan | Nereden gelir |
|---|---|---|
| Garanti | ref_ret_num |
provizyon yanıtı Transaction.RetrefNum |
| Yapı Kredi | host_ref_num |
provizyon yanıtı hostlogkey |
| PayFlex | transaction_id |
provizyon yanıtı TransactionId |
| Vakıf Katılım | remote_order_id |
provizyon yanıtı OrderId |
new RefundPaymentData('SIPARIS-123', 49.90, metadata: ['ref_ret_num' => '...']);
cancel()vestatus()şu anPaymentGatewayInterface'de değil, driver'lara özel metotlardır. Yani statik tip güvenliği yoktur; desteklemeyen bir driver'da çağırırsanız runtime'da patlar. Hangi driver'ın hangisini desteklediği tabloda yazıyor.
Ödeme modelleri
| Sabit | Ne yapar | Ne zaman |
|---|---|---|
MODEL_3D_SECURE |
Doğrulama sonrası ayrı provizyon isteği | Varsayılan; en yaygın |
MODEL_3D_PAY |
Doğrulama ve provizyon tek adımda bankada | Daha az round-trip isteyen kurulumlar |
MODEL_3D_HOST |
Kart formu da bankada toplanır | Kart verisi sunucunuza hiç uğramaz — PCI kapsamını daraltır |
MODEL_NON_SECURE |
3D yok, doğrudan provizyon | Mail order / abonelik |
3D Host modelinde card vermeniz gerekmez.
Yetenekler
Her banka her işlemi sunmaz. Bu bir eksiklik değil, sağlayıcı sınırıdır: PayTR iptal (void) API'si sunmaz, Akbank'ın yeni API'si tekil durum sorgusu sunmaz, Kuveyt Türk ön provizyon sunmaz.
Paket bunu tip düzeyinde bildirir — desteklenmeyen bir metodu çağırmadan
önce instanceof ile kontrol edin:
use Voxyfy\AnadoluPay\Contracts\SupportsStatusQuery; $gateway = AnadoluPay::driver('garanti'); if ($gateway instanceof SupportsStatusQuery) { $status = $gateway->status('SIPARIS-123'); }
| Driver | Durum | İptal | Ön prov. | Geçmiş | BIN | Taksit | Tekrar |
|---|---|---|---|---|---|---|---|
akbank, isbank, ziraat, halkbank, qnb, teb, sekerbank, ing, alternatifbank, turkiyefinans |
✅ | ✅ | ✅ | ✅ | — | — | ✅ |
garanti |
✅ | ✅ | ✅ | ✅ | ✅ | — | ✅ |
yapikredi, albaraka |
✅ | ✅ | ✅ | — | — | — | — |
vakifbank, ziraat-payflex |
✅ | ✅ | ✅ | — | — | — | ✅ |
denizbank |
✅ | ✅ | ✅ | — | — | — | — |
qnb-payfor, ziraat-katilim |
✅ | ✅ | ✅ | ✅ | — | — | — |
kuveytturk |
✅ | ✅ | — | — | — | — | — |
vakif-katilim |
✅ | ✅ | ✅ | ✅ | — | — | — |
akbank-pos |
— | ✅ | ✅ | ✅ | — | — | ✅ |
paytr |
✅ | — | — | — | ✅ | ✅ | — |
param |
✅ | ✅ | ✅ | — | — | — | — |
tosla |
✅ | ✅ | ✅ | ✅ | — | ✅ | — |
craftgate |
✅ | — | ✅ | ✅ | ✅ | ✅ | — |
moka |
✅ | ✅ | ✅ | ✅ | ✅ | — | — |
paratika |
✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — |
iyzico |
✅ | — | — | — | ✅ | ✅ | — |
Arayüzler: SupportsStatusQuery, SupportsCancellation,
SupportsPreAuthorization, SupportsOrderHistory, SupportsBinQuery,
SupportsInstallmentQuery, SupportsRecurringPayments.
Durum sorgusu
Zaman aşımı gibi belirsiz durumları kapatmanın tek yolu budur.
$status = AnadoluPay::driver('garanti')->status('SIPARIS-123'); $status->found; // banka bu siparişi tanıyor mu $status->isPaid(); // para tahsil edildi mi $status->isPending(); // 3D doğrulaması bekleniyor $status->amount; // Money $status->refundedAmount;
Bankaların birbirine benzemeyen durum kodları (A, 1, SUCCESS,
Başarılı) tek bir sözlüğe indirgenir. Tanınmayan bir kod unknown
döner — sessizce "başarılı" sayılmaz.
Ön provizyon isPaid() için false döndürür: tutar bloke edilmiştir ama
tahsil edilmemiştir.
Ön provizyon
$gateway = AnadoluPay::driver('garanti'); // Bloke et $response = $gateway->preAuthorize($data); // Nihai tutar belli olunca tahsil et $gateway->capture(new CapturePaymentData( orderId: 'SIPARIS-123', amount: 149.90, // blokeden küçük olabilir metadata: ['ref_ret_num' => '...'], // Garanti bu referansı ister ));
Bloke süresiz değildir; bankaya göre 1-30 gün içinde kapatılmazsa düşer.
Taksit ve BIN
$bin = AnadoluPay::driver('iyzico')->binLookup('415565'); $bin->bankName; // "Garanti BBVA" $bin->isCredit(); $options = AnadoluPay::driver('paytr')->installmentOptions( Money::fromMinorUnits(19990), ); foreach ($options as $option) { $option->count; // 3 $option->totalPrice; // Money — komisyon dâhil $option->monthlyPrice; }
BIN sorgusuna kart numarasının tamamını göndermeyin; ilk 6-8 hane yeter.
Tekrarlayan ödeme
Plan ilk ödemeyle birlikte bankaya bildirilir; sonraki çekimleri banka kendisi başlatır.
new CreatePaymentData( amount: 49.90, // … metadata: ['recurring' => new RecurringPlan( interval: 1, frequency: RecurringPlan::FREQUENCY_MONTH, paymentCount: 12, )], );
Desteklenen frekanslar bankaya göre değişir — Garanti yıllık, PayFlex
haftalık tekrar sunmaz. supportedRecurringFrequencies() ile sorgulayın;
desteklenmeyen bir frekans PaymentFailedException fırlatır.
Tutarlar
Tutarlar paket içinde her zaman kuruş cinsinden tam sayı olarak taşınır.
0.1 + 0.2 !== 0.3 olduğu için float ile hesaplanan bir tutar imzaya giren
dizgiyi bir kuruş kaydırabilir ve banka işlemi reddeder.
use Voxyfy\AnadoluPay\Support\Money; new CreatePaymentData(amount: Money::fromMinorUnits(19990), ...); // 199,90 TL new CreatePaymentData(amount: 199.90, ...); // eşdeğer
float vermek çalışmaya devam eder — iki ondalık haneye yuvarlanıp kuruşa
çevrilir. Tutarı zaten kuruş olarak tutuyorsanız (veritabanında int kolon
gibi) Money::fromMinorUnits() kesinlik kaybı olmayan tek yoldur.
Driver'lar tutara yalnızca $data->money() üzerinden erişir ve bankanın
istediği biçime orada çevirir:
| Örnek (199,90 TL) | Kullanan | |
|---|---|---|
toMinorUnitsString() |
"19990" |
Garanti, PosNet, Kuveyt Türk, Tosla |
toDecimalString() |
"199.90" |
Akbank POS, PayFlex, iyzico |
toNaturalString() |
"199.9" |
NestPay, PayFor, InterPos |
Bankaların tuhaflıkları
Driver'lar bunları sizin için hallediyor. Burada olmalarının sebebi, bir şey ters gittiğinde nereye bakacağınızı bilmeniz.
Tutar formatı üç farklı. Garanti, PosNet, Kuveyt Türk ve Tosla kuruş
cinsinden tam sayı ister (199.90 → 19990). NestPay ve PayFor PHP'nin doğal
float gösterimini ister (199.9 → "199.9", 100.0 → "100"). Akbank POS ve
PayFlex iki ondalıklı dizgi ister ("199.90"). Hash tam olarak gönderilen
dizgi üzerinden hesaplandığı için bu formatlar değiştirilemez.
Taksit alanı tek çekimde bile dört farklı. NestPay boş dizgi, PosNet '00',
PayFor ve Kuveyt Türk '0', Param '1' bekler. PayFlex alanı hiç göndermez.
Para birimi kodu her yerde ISO 4217 sayısal değil. Kuveyt Türk dört haneli
kullanır (0949), PosNet V1 harf kısaltması (TL, US, EU).
PayFor aynı gün içindeki işlemi iade ettirmez. Gün sonu kapanmadan
Refund denenirse banka V014 ("Bu işlem geri alınamaz, lütfen asıl işlemi
iptal edin.") döndürür; aynı gün için cancel() kullanın. Kart hamiline para
iadesi açısından ikisi aynı sonucu verir, sadece muhasebe kaydı farklıdır.
PayFlex'te enrollment adımı diğer uçlardan ayrışır. Provizyon ve sorgu
istekleri prmstr alanında URL-kodlanmış XML ister; enrollment ise düz form
alanı bekler. XML gönderilirse banka alanları hiç okumadan yanıltıcı bir
2030 Invalid expire date döndürür. Son kullanma tarihi de iki biçimdedir:
enrollment YYMM, provizyon YYYYMM.
PayFlex 3D'de PaReq her zaman klasik bir 3DS bloğu değildir. BKM "GO
Güvenli Öde" kurulumunda base64'ü, kendi kendini gönderen bir HTML sayfasıdır ve
doğrulama sayfası ACSUrl'de değil o sayfanın form hedefindedir; ACSUrl'e
POST edilirse banka "400 Hatalı İstek" sayfası verir. Driver bunu ayırt eder.
PayFlex 3D provizyonu kart bilgisi istemez. Banka işlemi MpiTransactionId
üzerinden bulur; bazı kurulumlar kart gönderilmesini 1127 ile reddeder. Bu
yüzden verify() çağrısında order['card'] isteğe bağlıdır — vermezseniz kart
alanları hiç gönderilmez ve PAN'ı istekler arasında saklamanız gerekmez.
PayFlex durum sorgusu tek bir durum alanı döndürmez. IsCanceled,
IsReversed, IsRefunded, TotalRefundAmount ve IsCaptured bayraklarından
türetilir. Kısmî iade edilmiş bir işlem paid kalır; refunded yalnızca tamamı
iade edildiğinde döner.
Garanti iade için ayrı kullanıcı ister. refund ve void işlemlerinde
securityData normal şifreyle değil iade şifresiyle hesaplanır. İki ayrı
kullanıcı tanımlamazsanız iadeler reddedilir.
Garanti bayi terminalleri bayi kodu ister. Üye işyeriniz banka tarafında
bayi (alt üye işyeri) yapılandırmasıyla açıldıysa her finansal istekte bayi
kodu zorunludur; gitmezse işlem 0809 — "SubMerchantID alanında bayii kodunun
gönderilmesi zorunludur" — ile reddedilir. GARANTI_SUB_MERCHANT_ID verildiğinde
paket alanı provizyon, 3D form, provizyon kapama, iptal/iade ve sorgu
isteklerine ekler; boş bırakılırsa istekler alanı hiç içermez. Alan imzaya
girmediği için mevcut kurulumların hash'i değişmez. XML isteklerinde alan
bankanın dokümanına uygun olarak Terminal düğümünün içine, 3D form post'unda
submerchantid alanı olarak yazılır; banka farklı bir düğüm isterse
GARANTI_SUB_MERCHANT_ID_PATH ile taşıyabilirsiniz.
Bankanın bayi tanımında ayrıca her bayinin kullanacağı kart numaraları da tanımlanır: gönderilen kart o bayi altında kayıtlı değilse işlem reddedilir. Bayi ve kart tanımları sanal POS yönetim ekranından (tam yetkili kullanıcı, Yönetim > Bayii tanımlama / Bayii kart tanımlama) yapılır.
PosNet üç sunucu isteği yapar. Önce oosRequestData ile veri paketleri
alınır, sonra 3D geçidine POST edilir, dönüşte oosResolveMerchantData ile
çözülüp oosTranData ile provizyon tamamlanır. Sipariş numarası 20 haneye
sıfırla doldurulur; iade/iptalde 24 hane olur ve 3D siparişler TDSC ön eki
alır.
Ziraat Katılım'ın dönüş hash'i banka tarafında tutarsız üretiliyor. Bu
yüzden o preset'te verify_hash varsayılan olarak kapalıdır. Bankanız
düzelttiyse ZIRAAT_KATILIM_VERIFY_HASH=true yapın.
PayTR ve Moka bildirimi OK yanıtı bekler. Webhook'unuz gövdede düz metin
OK döndürmezse bildirim tekrar tekrar gönderilir (Moka iki kez daha dener).
Paratika iki hash alanı döndürür ve biri kullanımdan kalkmıştır.
SD_SHA512 örnek yanıtlarda önce görünür ama dokümanda "Do not use!"
işaretlidir; doğrusu sdSha512dir. Ayrıca durum sorgusu bir sipariş için
tüm işlemleri döndürür; sadece satış kaydına bakarsanız iade edilmiş
sipariş "ödendi" görünür.
Tosla zaman damgasını Türkiye saatinde ister. timeSpan GMT+3'te ve
en fazla 1 saat farkla kabul edilir. UTC'de çalışan bir uygulamada damga üç
saat geride kalır ve her istek 998 Validasyon Hatası alır — mesaj sebebi
söylemez. Paket damgayı Europe/Istanbul üretir.
Moka'da üç ayrı kimlik vardır ve birbirinin yerine geçmezler:
| Alan | Nedir | Nerede kullanılır |
|---|---|---|
OtherTrxCode |
Sizin sipariş numaranız | durum sorgusu, iptal, iade |
VirtualPosOrderId / trxCode |
Moka'nın işlem kodu (3D dönüşünde gelir) | iptal, iade |
DealerPaymentId |
Moka'nın sayısal ödeme kaydı | yalnızca detay sorgusu |
Paket verdiğiniz değeri sipariş numaranız sayar; Moka'nın kendi kodunu
kullanacaksanız metadata['virtual_pos_order_id'] ile bildirin. Biçime bakarak
tahmin edilmez: kod dokümanda ORDER-… görünse de gerçek bir bayide
Test-df91b14d-… biçiminde geldi.
Durum sorgusundan dönen paymentId DealerPaymentId'dir; onu iptal veya
iadeye verirseniz PaymentNotFound alırsınız.
Moka'da durum sorgusu ödeme numarasını değil sipariş numaranızı ister.
GetDealerPaymentTrxDetailList ucu OtherTrxCode (sizin sipariş numaranız)
ya da PaymentId (Moka'nın sayısal kaydı) kabul eder. Dönüşteki trxCode
bunların hiçbiri değildir — iptal ve iade için kullanılır.
Moka'da her banka her bayide tanımlı değildir. Sanal POS'u tanımlanmamış
bir bankanın kartıyla ödeme başlatırsanız VirtualPosNotAvailable alırsınız.
Hata kartın bankasıyla ilgilidir — tutar veya taksit değiştirmek çözmez.
Moka'da BIN sorgusu farklı bir sarmalayıcı ister. Diğer servisler
PaymentDealerRequest, BIN sorgusu BankCardInformationRequest bekler.
Paket bunu kendisi ayırır; kendi isteğinizi yazıyorsanız dikkat edin.
Moka başarıyı ayrı bir alanda söylemez. 3D dönüşündeki resultCode
başarılı işlemlerde boş gelir; sonuç hashValue içindedir. Ödeme başlatılırken
dönen CodeForHash saklanmazsa dönüş yorumlanamaz.
Param IP kısıtı uygular ve eski test adresi kapandı. posws ve
testposws sunucuları whitelist dışındaki adreslerden gelen isteği WAF
seviyesinde 403 ile reddeder — yani kimlik bilgileriniz doğru olsa bile
sunucunuzun IP'si Param'a bildirilmeden hiçbir çağrı geçmez. Ayrıca çok
sayıda kaynakta geçen test-dmz.param.com.tr adresi artık 404 dönüyor;
güncel test adresi testposws.param.com.tr'dir.
⚠️ Param'ın (Türk Elektronik Para A.Ş.) altyapısı fiilen kapalı — IP
kısıtı bunun bir sonucu, sebebi değil. TCMB 2026-04-30'da Param'ın ödeme
hizmetleri ve e-para ihraç yetkisini geçici olarak durdurdu; 2026-05-04'te
mahkeme yürütmeyi durdurunca şirket kesintisiz çalışmaya devam etti, ama
2026-07-13'te mahkeme bu kararı geri çekti ve TMSF, İstanbul Cumhuriyet
Başsavcılığı soruşturması kapsamında Param ve grup şirketlerine kayyum
olarak atandı. 2026-08-05 tarihli basın duyurusunda TMSF, ParamPOS üye
işyeri alacaklarının 100.000 TL'ye kadarının ödeneceğini açıkladı — yani
POS hizmeti normal işlemiyor, tasfiye/iade süreci işliyor. Bu durumdayken
param driver'ını yeniden doğrulamaya çalışmak (yeni IP bildirmek, test
üye işyeri istemek) anlamsız: muhatap kurumun kendisi kayyım denetiminde.
Kod repoda kalıyor ama aktif olarak sürdürülmüyor.
Craftgate iki ayrı anahtar kullanır. API istekleri Secret Key ile, 3D dönüşü panelde ayrıca üretilen 3D Secure Callback Key ile imzalanır. İkisini karıştırmak "imza geçersiz" hatası verir. Webhook'ların üçüncü bir anahtarı vardır (Merchant Hook Key).
Craftgate'te ödeme formu POST edilmez. 3ds-init ucu hazır bir HTML
sayfası döner; PaymentResponse::$htmlContent içinde gelir ve doğrudan
tarayıcıya basılır. Ayrıca kısmi iade ödeme değil işlem bazındadır:
metadata['payment_transaction_id'] vermezseniz paket sessizce tam iade
yapmak yerine hata verir.
Taşıma hatalarında iki ayrı soru vardır. safeToRetry isteğin bankaya
ulaşmadığından emin miyiz, outcomeUncertain ise işlemin gerçekleşmiş olma
ihtimali var mı demektir. Banka isteği okumadan reddettiyse (4xx) sonuç
kesindir — hiçbir şey olmamıştır; zaman aşımı ve 5xx'te ise durum sorgusuyla
teyit gerekir.
Kuveyt Türk'te sorgu ve iade ayrı bir SOAP servisindedir. Ödeme ve
provizyon XML uçlarına giderken durum sorgusu, iade ve iptal
VirtualPosService.svc/Basic adresine gider. Bu uç JSON gövdeyi 415 ile
reddeder ve paketin bu bölümü henüz doğrulanmamıştır. Ayrıca query_api
tanımlanmazsa varsayılan canlı adrestir — test terminaliyle çalışırken mutlaka
test adresini verin.
NestPay sorgu yanıtında tutarlar kuruş cinsindendir. Birleşik
ORDERSTATUS alanındaki ORIG_TRANS_AMT ve CAPTURE_AMT kuruştur; ondalık
sanılırsa tutar yüz katı raporlanır. Paket bunu ayırır.
NestPay dönüşünde boş alanlar null olmamalı. Banka boş alanları hash'e
boş dizgi olarak katar. Laravel'in varsayılan ConvertEmptyStringsToNull
middleware'i bunları null yapar; paket null'ı boş dizgi sayarak bunu
telafi eder. Dönüş yükünü kendiniz işliyorsanız aynı kurala uyun, yoksa imza
hiçbir zaman tutmaz.
Bankanın dönüş POST'u siteler arasıdır. SameSite=lax çerezi bu istekte
gönderilmez; dönüşte oturum boş gelir. Sipariş bağlamını oturumda değil,
okUrlin sorgu dizgisinde taşıyın. Doğrulamaya yalnızca POST gövdesini verin —
sorgu parametreleri bankanın imzasına dâhil değildir.
NestPay hash'i alanları sıralar. Alanlar doğal sırada (harf duyarsız)
sıralanır, hash/encoding/nationalidno çıkarılır, sona secret key eklenir,
| ve \ karakterleri kaçırılır. Forma yeni bir alan eklerseniz hash'e de
girer — banka bunu bilmiyorsa işlem reddedilir.
Hata yönetimi ve yeniden deneme
Ödeme entegrasyonlarında en tehlikeli hata, belirsiz olandır. Banka "reddettim" derse ne yapacağınız bellidir; ama istek zaman aşımına uğradığında paranın çekilip çekilmediğini bilmezsiniz. Paket bu ikisini tip düzeyinde ayırır:
AnadoluPayException
├── PaymentFailedException kesin: banka isteği aldı ve reddetti
├── InvalidSignatureException imza tutmadı — sahte callback olabilir
├── DuplicatePaymentException aynı sipariş için ikinci deneme
├── UnsupportedOperationException driver bu işlemi desteklemiyor
├── DriverNotFoundException yapılandırma hatası
└── TransportException BELİRSİZ: istek ulaştı mı, işlendi mi?
├── GatewayUnreachableException bağlantı kurulamadı / zaman aşımı
└── GatewayHttpException 2xx dışı yanıt veya çözümlenemeyen gövde
TransportException yakaladığınızda ödemeyi başarısız saymayın — durumu
banka üzerinden sorgulayın veya müşteriye "işleminiz kontrol ediliyor" deyin.
use Voxyfy\AnadoluPay\Exceptions\PaymentFailedException; use Voxyfy\AnadoluPay\Exceptions\TransportException; try { $result = AnadoluPay::driver('garanti')->verify($data); } catch (PaymentFailedException $e) { // Kesin ret: siparişi iptal edebilirsiniz. } catch (TransportException $e) { // Belirsiz: siparişi "beklemede" bırakın, durum sorgusuyla teyit edin. $e->safeToRetry; // yalnızca isteğin bankaya ulaşmadığı kesinse true }
Yeniden deneme
ANADOLUPAY_RETRY_TIMES=2 ANADOLUPAY_RETRY_SLEEP_MS=250
Retry yalnızca bankaya ulaşılamayan durumlarda yapılır: bağlantı reddedildi, DNS çözülemedi, TLS kurulamadı. Bu hatalarda isteğin bankaya varmadığı bilinir.
Zaman aşımı ve HTTP hataları tekrar denenmez. Her ikisinde de istek bankaya ulaşmış ve işlenmiş olabilir; körlemesine ikinci bir ödeme isteği göndermek çift çekim demektir. Bu davranış testle kilitlidir.
Varsayılan 0dır — yani retry kapalıdır. Açmadan önce sipariş durumunu kendi
tarafınızda takip ettiğinizden emin olun.
Event'ler
Ödeme akışının dört noktasında event yayınlanır. Hiçbiri kart verisi taşımaz, çünkü dinleyicilerin çoğu bu veriyi loglar veya kuyruğa yazar.
| Event | Ne zaman | Taşıdığı |
|---|---|---|
PaymentInitiated |
müşteri bankaya yönlendirilmeden önce | driver, orderId, Money, model, taksit |
PaymentVerified |
dönüş doğrulanıp provizyon tamamlanınca | driver, orderId, paymentId, success, status |
PaymentFailed |
akış bir istisnayla kesilince | driver, orderId, reason, exception |
RefundIssued |
iade isteği gönderilince | driver, paymentId, Money, refundId, success |
Event::listen(PaymentVerified::class, function (PaymentVerified $event) { if ($event->success) { Order::where('code', $event->orderId)->update([ 'status' => 'paid', 'payment_reference' => $event->paymentId, ]); } });
PaymentVerified success: false ile de gelebilir — bu, doğrulama akışının
hatasız tamamlandığı ama ödemenin alınmadığı anlamına gelir. PaymentFailed
ise akışın kesildiği durumdur; istisna yutulmaz, event'ten sonra yukarı çıkar.
ANADOLUPAY_EVENTS=false ile kapatılabilir.
Mükerrer ödeme koruması
ANADOLUPAY_IDEMPOTENCY=true ANADOLUPAY_IDEMPOTENCY_TTL=30
Aynı sipariş numarası için pencere içinde ikinci bir createPayment()
çağrısı DuplicatePaymentException fırlatır. Asıl hedef kullanıcının "Öde"
düğmesine iki kez basmasıdır.
Pencere bilinçli olarak kısadır (varsayılan 30 sn): ödeme gerçekten başarısız olduğunda müşterinin aynı sipariş numarasıyla tekrar denemesi meşrudur. Başlatma isteği hata alırsa kilit hemen bırakılır.
Kilit Cache::add() ile alınır — atomiktir, yani iki eşzamanlı istekten
yalnızca biri geçer. Bunun çalışması için array dışında bir cache sürücüsü
(redis, memcached, database) gerekir.
Bu bir kolaylıktır, kesin garanti değildir. Mükerrer çekime karşı asıl savunma, siparişin durumunu kendi veritabanınızda tutmak ve ödemesi alınmış siparişler için akışı hiç başlatmamaktır.
Loglama
Banka bir işlemi reddettiğinde size yalnızca bir kod döner
(ProcReturnCode=99). Sorunun hangi alanda olduğunu ancak gönderdiğiniz
gövdeyi görerek anlarsınız. Entegrasyon geliştirirken açın:
ANADOLUPAY_LOGGING=true ANADOLUPAY_LOG_CHANNEL=anadolupay # boşsa uygulamanın varsayılan kanalı
[debug] AnadoluPay banka isteği {"bank":"garanti","url":"…","body":"<GVPSRequest>…
<Number>415565******6111</Number>
<CVV2>[gizlendi]</CVV2>…"}
[debug] AnadoluPay banka yanıtı {"bank":"garanti","status":200,"duration_ms":412,…}
Maskeleme iki katmanlıdır. Birincisi alan adına göre (cvv, password,
secret_key…). İkincisi değerin biçimine göre: Luhn kontrolünden geçen her
13–19 haneli sayı, alan adı ne olursa olsun maskelenir. İkinci katman
olmadan, on altı driver'ın farklı adlandırdığı kart alanlarından birini
gözden kaçırmak kart verisini loga düşürürdü.
Luhn kontrolü yanlış pozitifleri de eler — PosNet'in 20 haneye doldurulmuş
sipariş numaraları ve NestPay'in MD taşıyan Number alanı okunabilir kalır.
Başarısız HTTP yanıtları warning, gerisi debug seviyesindedir.
Loglama varsayılan olarak kapalıdır. Maskeleme uygulansa bile bu kayıtların nereye yazıldığı bilinçli bir tercih olmalıdır: kalıcı bir kanal seçiyorsanız erişimini kısıtlayın ve saklama süresi tanımlayın.
iyzico
iyzico bankalardan iki noktada ayrılır ve paket ikisini de sizin yerinize halleder:
- 3D adımında form alanı değil, base64 kodlanmış hazır bir HTML sayfası döner.
Paket bunu çözer;
toHtmlForm()doğrudan basılabilir HTML verir. - Her yanıt, callback ve webhook ayrı bir imza şeması kullanır. Üçü de HMAC-SHA256 üretir ve sonucu onaltılık kodlar:
| İmza | Nerede | İmzalanan |
|---|---|---|
Authorization |
istek başlığı | randomKey + uriPath + gövde → IYZWSv2 base64(apiKey:…&randomKey:…&signature:…) |
| Yanıt / callback | gövdedeki signature |
uca göre değişen alanlar, : ayraçlı |
| Webhook | X-IYZ-SIGNATURE-V3 başlığı |
secretKey + eventType + …, ayraçsız |
Yanıt imzasında alan sırası uca göre sabittir; örneğin 3DS callback'i
conversationData:conversationId:mdStatus:paymentId:status sırasını kullanır.
Tutarlardaki sondaki sıfırlar imzadan önce atılır (10.50 → 10.5).
IYZICO_API_KEY=xxx IYZICO_SECRET_KEY=xxx IYZICO_BASE_URL=https://sandbox-api.iyzipay.com IYZICO_CALLBACK_URL=https://shop.test/anadolupay/webhook/iyzico
İade /v2/payment/refund ucundan yapılır:
AnadoluPay::driver('iyzico')->refund(new RefundPaymentData( paymentId: '12345', amount: 49.90, metadata: ['conversation_id' => 'SIPARIS-123'], ));
iyzico'da tutar zorunludur. Diğer driver'larda tutarı boş bırakmak
"tamamını iade et" demektir; iyzico'da böyle bir uç yoktur ve tutarsız istek
5004 price gönderilmesi zorunludur ile reddedilir. Paket bu durumda ödemenin
tutarını /payment/detail ucundan okuyup gönderir — yani tutarsız çağrı da
çalışır, ama arka planda fazladan bir sorgu yapar.
Kısmi iade yapılmış bir ödemede okunan tutar kalan bakiyeden büyük olur ve iyzico işlemi reddeder. Bu bilinçli bir tercihtir: fazla iade etmektense hata vermek doğrudur. Öyle bir ödemede tutarı açıkça verin.
Ayrı bir iptal işlemi yoktur. Driver SupportsCancellation uygulamaz;
çünkü iyzico aynı gün yapılan tam iadeyi kendisi iptal olarak işler — yanıtta
transactionType: CANCEL döner. Gün içi iptal için refund() çağırın.
Paratika
Paratika, NestPay driver'larının arkasındaki Payten/Asseco'nun kendi ödeme kuruluşudur. İstek imzası kullanmaz; kimlik doğrulama her isteğe eklenen üç alandır. İmza yalnızca 3D dönüşünde vardır.
Akış her modelde bir oturum anahtarıyla başlar. Paket bunu sizin yerinize yapar; dört ödeme modeli dört farklı uca karşılık gelir:
| Model | Ne olur |
|---|---|
3d_pay |
Tarayıcı post/sale3d/{token}a POST eder; Paratika hem 3D doğrulamayı hem satışı yapar |
3d |
Tarayıcı post/auth3d/{token}a POST eder; yalnızca doğrulama yapılır, satış dönüşte tamamlanır |
3d_host |
Müşteri Paratika'nın ödeme sayfasına yönlendirilir |
regular |
Kart bilgisiyle doğrudan SALE |
Dönüş imzasında iki alan gelir ve biri tuzaktır. Örnek yanıtlarda SD_SHA512
önce görünür ama dokümanda "Deprecated / Legacy — Do not use!" diye
işaretlidir. Paket güncel olanı doğrular:
sdSha512 = sha512_hex( merchantPaymentId|customerId|sessionToken|responseCode|random|secretKey )
PARATIKA_SECRET_KEY, API şifresinden farklı bir değerdir.
Durum sorgusu bir liste döndürür. Paratika bir sipariş numarasına ait
tüm işlemleri verir: satış, iade, iptal. İade edilmiş bir satışın kendi
kaydı hâlâ AP (onaylı) görünür — tek kayda bakan entegrasyon iade edilmiş
siparişi "ödendi" sanır. Paket listenin tamamını yorumlar: tam iade
refunded, kısmi iade paid + refundedAmount, iptal cancelled.
$status = AnadoluPay::driver('paratika')->status('SIPARIS-123'); $status->isPaid(); // kısmi iadeden sonra da true $status->refundedAmount; // Money|null
İade tutarı verilirse Paratika bunu kendi tarafında PTREFUND olarak
kaydeder; ayrı bir aksiyon göndermeniz gerekmez.
PARATIKA_MERCHANT=xxx PARATIKA_MERCHANT_USER=api@shop.test PARATIKA_MERCHANT_PASSWORD=xxx PARATIKA_SECRET_KEY=xxx PARATIKA_PAYMENT_API=https://entegrasyon.paratika.com.tr/paratika/api/v2
Moka United
Moka'nın kimlik doğrulaması bir istek imzası değil, sabit bir paroladır:
CheckKey = sha256( DealerCode + "MK" + Username + "PD" + Password )
Her istekte aynı değer gider — yani gövdeyi korumaz. Bu paketin diğer driver'larındaki hash'lerden farkı budur.
Asıl dikkat edilmesi gereken yer 3D dönüşü. Moka ödemenin başarılı olup
olmadığını ayrı bir alanda söylemez; dönüşteki resultCode başarılı
işlemlerde boş gelir. Sonuç yalnızca hashValue içinde taşınır:
hashValue = sha256( CodeForHash + "T" ) → başarılı
hashValue = sha256( CodeForHash + "F" ) → başarısız
CodeForHash ödeme başlatılırken bir kez döner. Saklamazsanız dönüşü
yorumlayamazsınız — bu yüzden paket sonucu tahmin etmeye çalışmaz, hata verir:
$response = AnadoluPay::driver('moka')->createPayment($data); // Bu değeri siparişle birlikte saklayın. $codeForHash = $response->raw['code_for_hash']; return redirect()->away($response->redirectUrl);
Dönüşte:
$result = AnadoluPay::driver('moka')->verify(new VerifyPaymentData( $request->all(), order: ['code_for_hash' => $codeForHash], ));
Hash ne T ne F varyantıyla eşleşiyorsa InvalidSignatureException atılır.
İptal ve iade ayrı uçlardır ve aynı şey değildir. Aynı gün saat 22.00'ye
kadar cancel() (DoVoid) işlemi anında iptal eder. refund()
(DoCreateRefundRequest) ise bir iade talebi oluşturur: yanıtta
RefundRequestId döner, ödemenin durumu hemen değişmez ve RefAmount bir
süre 0 kalır. Gerçek test servisinde ölçüldü — talep başarıyla kabul
edildikten sonra sipariş hâlâ paid görünüyordu.
Yani refund() başarılı dönmesi "para geri gitti" demek değil, "talep
alındı" demektir. Aynı gün geri ödeme istiyorsanız cancel() kullanın.
İade tutarı verilmezse kalan tutarın tamamı talep edilir.
Referans olarak hem Moka'nın numarası hem sizinki kullanılabilir; paket
ORDER- ile başlayan değerleri Moka'nın numarası (VirtualPosOrderId),
diğerlerini kendi sipariş numaranız (OtherTrxCode) sayar.
MOKA_DEALER_CODE=xxx MOKA_USERNAME=xxx MOKA_PASSWORD=xxx MOKA_PAYMENT_API=https://service.refmokaunited.com
Moka da PayTR gibi bildirimlere düz metin OK yanıtı bekler; paketin webhook
rotası bunu kendisi döndürür.
Craftgate
Craftgate tek bir bankanın sanal POS'u değil, birden çok POS'u tek API
arkasında toplayan bir orkestrasyon platformudur. Akış bu yüzden banka
driver'larından ayrılır: müşteri bir banka geçidine form POST edilmez,
3ds-init ucu hazır bir HTML sayfası döner.
$response = AnadoluPay::driver('craftgate')->createPayment($data); return response($response->htmlContent); // 3D sayfası
Üç ayrı anahtar kullanılır; hangisinin nerede kullanıldığını karıştırmak en sık yapılan hatadır:
| Anahtar | Nerede | İmzalanan |
|---|---|---|
| Secret Key | x-signature istek başlığı |
baseUrl + path + apiKey + secretKey + rndKey + gövde → base64(sha256(…)) |
| 3D Secure Callback Key | 3D dönüşündeki hash alanı |
key###status###completeStatus###paymentId###conversationData###conversationId###callbackStatus → sha256 (onaltılık) |
| Merchant Hook Key | webhook imzası | eventType + eventTimestamp + status + payloadId → base64(hmac-sha256(…)) |
CRAFTGATE_API_KEY=xxx CRAFTGATE_SECRET_KEY=xxx CRAFTGATE_CALLBACK_KEY=xxx CRAFTGATE_HOOK_KEY=xxx CRAFTGATE_PAYMENT_API=https://sandbox-api.craftgate.io
Webhook imzasını driver doğrular:
$gateway->verifyWebhookSignature($request->header('X-Signature'), $request->all());
Kısmi iade işlem bazındadır. Craftgate bir ödemeyi birden çok satıcı işlemine bölebildiği için hangi işlemin iade edileceğini paket kendi başına seçemez:
AnadoluPay::driver('craftgate')->refund(new RefundPaymentData( paymentId: '12345', amount: 20.00, metadata: ['payment_transaction_id' => 555], ));
payment_transaction_id vermezseniz paket sessizce tam iade yapmaz, hata
verir. Tam iade için tutarı hiç göndermeyin. Gün içi iptal ayrı bir uç
değildir; Craftgate mutabakata girmemiş işlemi iade isteğinde kendisi void
olarak geçer.
Tami
⚠️ Henüz gerçek bir sandbox'a karşı doğrulanmadı. Aşağıdaki üç nokta dokümantasyondan (dev.tami.com.tr) çıkarıldı ama test edilmedi — sandbox kimlik bilgisi elinize geçtiğinde ilk iş bunları doğrulamak olmalı.
Tami de Craftgate gibi bir orkestrasyon platformu; /payment/auth ucu 3D
başlatmayı ve non-secure satışı birlikte karşılar, 3D doğrulaması banka
callbackUrl'e POST ettiğinde ise para ayrı bir /payment/complete-3ds
isteğiyle çekilir:
$response = AnadoluPay::driver('tami')->createPayment($data); return response($response->htmlContent); // 3D sayfası (base64 çözülmüş)
İki katmanlı kimlik doğrulama:
| Anahtar | Nerede | Formül |
|---|---|---|
secret_key |
PG-Auth-Token başlığı |
merchantNumber:terminalNumber:base64(sha256(merchantNumber+terminalNumber+secretKey)) |
username/password (JWK kid/k) |
Gövdedeki securityHash alanı |
HS512 ile imzalanmış JWT (JWS compact) — payload, securityHash alanı hariç gövdenin JSON'u |
TAMI_MERCHANT_NUMBER=xxx TAMI_TERMINAL_NUMBER=xxx TAMI_SECRET_KEY=xxx TAMI_JWK_KID=xxx TAMI_JWK_K=xxx TAMI_PAYMENT_API=https://sandbox-paymentapi.tami.com.tr
Bilinen sınırlar:
securityHashiçin JWT/JWS yorumu, dokümantasyonun "Security Hash Hesaplama" sayfasındaki somut kod örneğine (JWSObject/MACSigner) dayanır. Ama diğer sayfalardaki örneksecurityHashdeğerleri nokta içermiyor — düz bir HMAC digest'i gibi görünüyor, JWT gibi değil. Dokümantasyon bu noktada kendi içinde çelişkili.- Ön provizyon kapama ucu (
/payment/post-auth) doğrulanmadı;/payment/pre-authile aynı adlandırma kalıbından tahmin edildi. - 3D dönüş callback'inin imzası (
hashedData=base64(hmac_sha256( cardOrganization+cardBrand+cardType+maskedNumber+installmentCount+ currencyCode+txnAmount+orderId+systemTime+success, secretKey))) Tami'nin kendi dokümantasyonunda formülsüz bırakılmıştı; alan sırası ve anahtar bağımsız bir kaynaktan doğrulandı — Tami'nin resmî bir onayı değil, gerçek bir sandbox'a karşı hâlâ test edilmedi. - İptal (
cancel) ve iade (refund) dokümantasyonda aynı uca (/payment/reverse) gidiyor; ikisi arasında protokol seviyesinde bir fark bulunamadı.
Test ortamı
Test kartları için ayrı bir belge var: TEST-KARTLARI.md — iyzico, Garanti, Akbank, PayTR, Craftgate, Moka ve Paratika'nın listeleri (hata senaryosu kartları dahil), diğer bankalar için kartı nereden alacağınız. Her bölümde hangi kartla ölçüm yaptığımız ve hangisinden kaçınmanız gerektiği yazılıdır.
Preset'lerdeki uç noktalar canlı ortamı gösterir. Test için ilgili
*_PAYMENT_API / *_GATEWAY_3D değişkenlerini bankanızın test adresiyle
değiştirin ve *_TEST_MODE=true yapın.
Garanti, çoğu bankanın aksine test üye işyeri bilgilerini geliştirici portalında açıkça yayınlar — bankadan ayrıca bir şey istemeniz gerekmez. Bu değerlerle çalıştığı 2026-08-11'de doğrulandı:
GARANTI_MERCHANT_ID=7000679 GARANTI_TERMINAL_ID=30691297 GARANTI_USERNAME=PROVAUT GARANTI_PASSWORD=123qweASD/ GARANTI_SECRET_KEY=12345678 GARANTI_REFUND_USERNAME=PROVRFN GARANTI_REFUND_PASSWORD=123qweASD/
Ödeme ve iade iki ayrı kullanıcı ister (PROVAUT / PROVRFN); test
ortamında ikisinin şifresi de aynıdır. Test yönetim paneli
sanalpostest.garanti.com.tr
adresinde: kullanıcı 99999999999, parola Destek.9, şifre 147852.
Bu terminalde iade ve iptal banka tarafından reddedilir (05 /
RPC-05); ön provizyon da 14 verir. Driver'ın hatası değildir —
istek biçiminin on dört ayrı çeşitlemesi (tam/kısmi tutar,
OriginalRetrefNum ile ve olmadan, void/refund) aynı yanıtı verir,
terminalde bu yetkiler tanımlı değildir.
GARANTI_TEST_MODE=true GARANTI_PAYMENT_API=https://sanalposprovtest.garantibbva.com.tr/VPServlet GARANTI_GATEWAY_3D=https://sanalposprovtest.garantibbva.com.tr/servlet/gt3dengine AKBANK_MERCHANT_ID=100100000 AKBANK_USERNAME=AKTESTAPI AKBANK_PASSWORD=AKBANK01 AKBANK_SECRET_KEY=123456 AKBANK_PAYMENT_API=https://entegrasyon.asseco-see.com.tr/fim/api AKBANK_GATEWAY_3D=https://entegrasyon.asseco-see.com.tr/fim/est3Dgate AKBANK_GATEWAY_3D_HOST=https://entegrasyon.asseco-see.com.tr/fim/est3Dgate ISBANK_MERCHANT_ID=700655000200 ISBANK_USERNAME=ISBANKAPI ISBANK_PASSWORD=ISBANK07 ISBANK_SECRET_KEY=TRPS0200 ISBANK_PAYMENT_API=https://entegrasyon.asseco-see.com.tr/fim/api ISBANK_GATEWAY_3D=https://entegrasyon.asseco-see.com.tr/fim/est3Dgate TURKIYEFINANS_MERCHANT_ID=280000100 TURKIYEFINANS_USERNAME=TFKBAPI TURKIYEFINANS_PASSWORD=TFKB2828 TURKIYEFINANS_SECRET_KEY=TRPS2828 TURKIYEFINANS_PAYMENT_API=https://entegrasyon.asseco-see.com.tr/fim/api TURKIYEFINANS_GATEWAY_3D=https://entegrasyon.asseco-see.com.tr/fim/est3Dgate QNB_MERCHANT_ID=600100000 QNB_USERNAME=FINANSAPI QNB_PASSWORD=FINANS06 QNB_SECRET_KEY=123456 QNB_PAYMENT_API=https://entegrasyon.asseco-see.com.tr/fim/api QNB_GATEWAY_3D=https://entegrasyon.asseco-see.com.tr/fim/est3Dgate YAPIKREDI_PAYMENT_API=https://setmpos.ykb.com/PosnetWebService/XML YAPIKREDI_GATEWAY_3D=https://setmpos.ykb.com/3DSWebService/YKBPaymentService VAKIFBANK_PAYMENT_API=https://onlineodemetest.vakifbank.com.tr:4443/VposService/v3/Vposreq.aspx VAKIFBANK_GATEWAY_3D=https://3dsecuretest.vakifbank.com.tr:4443/MPIAPI/MPI_Enrollment.aspx DENIZBANK_PAYMENT_API=https://test.inter-vpos.com.tr/mpi/Default.aspx DENIZBANK_GATEWAY_3D=https://test.inter-vpos.com.tr/mpi/Default.aspx QNB_PAYFOR_PAYMENT_API=https://vpostest.qnb.com.tr/Gateway/XMLGate.aspx QNB_PAYFOR_GATEWAY_3D=https://vpostest.qnb.com.tr/Gateway/Default.aspx KUVEYTTURK_PAYMENT_API=https://boatest.kuveytturk.com.tr/boa.virtualpos.services/Home ALBARAKA_PAYMENT_API=https://epostest.albarakaturk.com.tr/ALBMerchantService/MerchantJSONAPI.svc TOSLA_PAYMENT_API=https://prepentegrasyon.tosla.com/api/Payment PARAM_PAYMENT_API=https://testposws.param.com.tr/turkpos.ws/service_turkpos_test.asmx AKBANK_POS_PAYMENT_API=https://apipre.akbank.com/api/v1/payment/virtualpos CRAFTGATE_PAYMENT_API=https://sandbox-api.craftgate.io MOKA_PAYMENT_API=https://service.refmokaunited.com PARATIKA_PAYMENT_API=https://entegrasyon.paratika.com.tr/paratika/api/v2
Sahte driver
Gerçek istek atmadan akışı denemek için fake driver'ını kullanın. Gerçek
driver'ların yetenek arayüzlerini uygular ve yaptığı işlemleri bellekte
tutar — ödeyip sonra status() sorarsanız gerçekten paid döner, iade
ederseniz refunded olur.
$gateway = AnadoluPay::driver('fake'); $gateway->createPayment($data); $gateway->status('SIPARIS-123')->isPaid(); // true $gateway->refund(new RefundPaymentData('SIPARIS-123')); $gateway->status('SIPARIS-123')->isRefunded(); // true
Varsayılan olarak her işlem başarılıdır; testlerin rastgele kırılmaması için sahte geçidin öngörülebilir olması gerekir. Hata yollarını denemek isterseniz:
config(['anadolupay.fake.success_rate' => 0]); // her zaman başarısız
Gerçek kartlarla denemek isterseniz TEST-KARTLARI.md.
Güvenlik
- Kart verisi (
CardData) saklanmamalıdır. Kendi loglarınızda göstermeniz gerekiyorsaCardData::masked()kullanın; paketin istek/yanıt logları zaten maskelidir. CardDatanesnelerinidd(),var_dump()veya exception raporlarına vermeyin — bunlar maskelemeden geçmez.verify_hashyalnızca bankanın hash'i tutarsız ürettiği bilinen kurulumlarda kapatılmalıdır. Kapalıyken sahte callback'lere açıksınızdır.verify_sslher zamantruekalmalıdır.- 3D Host modeli kart verisini sunucunuzdan tamamen uzak tutar; PCI kapsamını daraltmak istiyorsanız en iyi seçenektir.
Güvenlik açığı bildirimi: security@voxyfy.com
Yeni banka eklemek
AbstractBankGateway sınıfını genişletin ve yedi metodu implement edin:
class YeniBankaGateway extends AbstractBankGateway { protected function build3dFormFields(CreatePaymentData $data): array { … } protected function checkCallbackHash(array $payload): bool { … } protected function is3dAuthSuccess(array $payload): bool { … } protected function provision(array $payload): array { … } protected function mapCallbackResponse(array $payload): VerificationResponse { … } protected function mapProvisionResponse(array $payload, array $provision): VerificationResponse { … } protected function extractOrderId(array $payload): ?string { … } }
Sonra config/anadolupay.php içindeki banks dizisine bir preset ekleyin.
Akış, hata yönetimi, HTTP ve loglama temel sınıftan gelir.
İmza için test yazın. Sabit girdilerle üretilmiş bir özet değerine
kilitleyin — mevcut driver'ların hepsinde örneği var (tests/Bank/HashTest.php).
İmza sessizce bozulabilen tek şeydir.
Yol haritası
Bilinen eksikler. Bir maddeye başlamadan önce issue açmanız çakışmayı önler.
Öncelikli
-
iyzico imza şemasını doğrula.Üç şema da (Authorization, yanıt/callback, webhook) resmi dokümantasyondan teyit edilip düzeltildi ve testle kilitlendi. -
iyzico iadesi./v2/payment/refundile tam ve kısmi iade. -
Tutarları kuruş cinsinden tam sayıya taşı.Moneyvalue object;floatgirdi geriye dönük uyumlu olarak destekleniyor.
İşlem kapsamı
-
Yetenekler artık arayüzlerle bildiriliyor — bkz. Yetenekler.cancel()vestatus()'ü sözleşmeye taşı. -
EksikKuveyt Türk (ayrı SOAP servisi) ve Param eklendi. PayTR iptal API'si sunmuyor; sağlayıcı sınırı.cancel(). -
Eksik13 driver'a yayıldı. Akbank POS tekil durum sorgusu sunmuyor; yerine işlem geçmişi var.status(). -
Kuveyt Türk iade/iptal. -
Ön provizyon / provizyon kapama.12 driver. -
İşlem geçmişi, taksit oranı ve BIN sorgulama. -
Tekrarlayan ödeme.Asseco, Garanti, PayFlex, Akbank POS.
Doğrulama
-
iyzico uçtan uca doğrulaması.2026-08-09, sandbox 3D Secure satış: dört imza şeması da gerçek trafikle doğrulandı. Bkz. Doğrulama durumu. -
İlk banka terminali.Ziraat NestPay test terminalinde 3D hash'i ve API kimlik doğrulaması kabul edildi; durum sorgusunda bulunan bir kusur düzeltildi. -
NestPay'de 3D akışını tamamla.2026-08-09, Ziraat test terminali: tarayıcıdan 3D onayı, provizyon, durum sorgusu, iade ve iptal. Üç kusur çıktı (hash'tenullişlenmesi, bileşikORDERSTATUS, kuruş tutar). -
PayFlex için gerçek terminal testi.2026-08-10, VakıfBank'ın açık sandbox'ı: 3D Secure satış, non-3D satış, durum, iade, iptal, ön provizyon ve kapama. Dört kusur çıktı — bkz. CHANGELOG. - Kalan altyapılar için gerçek terminal testi — Garanti, PosNet, PayFor, InterPos. BOA'da yalnızca ödeme doğrulandı (Kuveyt Türk); sorgu/iade servisi SOAP olduğu için ölçülemedi. Kapsamı yatay büyütmek bu doğrulama yapılmadan riski azaltmıyor.
-
iyzico'nun işlem kapsamı.İade, durum, BIN ve taksit sorgusu sandbox'ta doğrulandı; iade tutarsız çağrıda hiç çalışmıyordu, düzeltildi. İptal ayrı bir işlem değil — iyzico tam iadeyi iptal olarak işliyor. - iyzico'nun diğer ödeme modelleri (
3d_pay,3d_host,regular) sandbox'ta çalıştırılmadı.
Yeni bankalar
Çoğu mevcut NestPay driver'ını kullanır; yeni kod değil, preset ve doğrulanmış uç nokta gerekir.
-
ING · Alternatif Bank · Türkiye Finans.Üçü de NestPay kullanıyor; uç noktaları doğrulandı (aşağıya bakın). - Anadolubank · Odeabank · Fibabanka · Burgan Bank · Emlak Katılım — sanal POS uç noktaları herkese açık bir kaynaktan doğrulanamadı. Tahmin edilen alan adları ya DNS'te yok ya da NestPay/PayFor/BOA imzası vermiyor. Bu bankalardan biriyle çalışıyorsanız entegrasyon dokümanınızdaki adresi issue olarak paylaşın, preset'i ekleyelim.
Preset eklenmemiş olması o bankayla çalışamayacağınız anlamına gelmez:
altyapısı bilinen bir bankanın uç noktalarını config/anadolupay.php
içinde kendiniz tanımlayabilirsiniz — bkz. Yeni banka eklemek.
Yeni ödeme kuruluşları
-
Craftgate.API imzası, 3D dönüş imzası ve webhook imzası Craftgate'in resmi istemci depolarındaki test vektörleriyle doğrulandı. -
Moka United.Dokümantasyonu tamamen açık; dönüş hash'i oradaki test vektörüyle kilitlendi. -
Paratika (Payten).Dokümantasyonu ve resmî örnek kodu açık. Test vektörü yayınlanmadığı için imza formülü dokümana göre uygulandı, ölçülmedi. -
Paycell.Eklendi. Resmî doküman bu arada yayımlandı (paycellapi.apidog.io); test kimlik bilgileri ve kart listesi herkese açık. Kart token adımı ve yanıt imzası sağlayıcının test ortamına karşı ölçüldü, 3D oturumu açıldı. Ödeme adımı ortak test üye işyerindeBank errorverdiği için doğrulanamadı — kendi üye işyeri kodunuzla denenmeli. - Sipay — imza şeması güvenilir bir public kaynaktan doğrulanamadığı için bilinçli olarak eklenmedi.
-
PayU Türkiye— eklenmeyecek. PayU'nun Türkiye'deki ödeme ucu (secure.payu.com.tr) artık DNS'te yok; PayU Türkiye'de iyzico markasıyla çalışıyor. İhtiyacınız olan driveriyzico. - Vallet
Altyapı
-
PSR-3 loglama (maskeli)— bkz. Loglama -
Event'ler— bkz. Event'ler -
Idempotency— bkz. Mükerrer ödeme koruması -
Retry politikası— bkz. Hata yönetimi ve yeniden deneme -
Hata sınıflandırması—TransportExceptionilePaymentFailedExceptionartık ayrı; aynı bölüme bakın.
Katkı
composer test # Pest composer format # Pint vendor/bin/phpstan # Larastan, level 5
Detaylar için CONTRIBUTING, sürüm geçmişi için CHANGELOG.
Lisans
MIT — bkz. LICENSE.
