Skip to content
ENTERPRISE Enterprise technology · Engineered for your business Enterprise Software · Managed Servers · Business E-mail · LiteSpeed + CloudLinux · Imunify360 Security · cPanel Control · Network profile up to 3 Gbps on eligible plans
AIOR

Virtual POS in Turkey: How It Works, 3D Secure and Integration Models

Aior

Administrator

Staff member
Joined
Apr 2, 2023
Messages
929
Reaction score
2
Points
18
Age
41
Location
Turkey
Website
aior.com
Thread owner

Sanal POS (virtual POS), kartlı ödemeyi fiziksel bir cihaz olmadan, internet üzerinden almanızı sağlayan üye işyeri altyapısıdır. Kartla ödeme almak isteyen bir e-ticaret sitesi, abonelik hizmeti ya da mobil uygulamanın önünde iki yol vardır: bir bankadan sanal POS almak ya da lisanslı bir ödeme veya elektronik para kuruluşunun altyapısını kullanmak. Hangisi seçilirse seçilsin ödeme büyük olasılıkla 3D Secure ile doğrulanır ve entegrasyon, banka dokümanlarında 3D, 3D Pay ya da 3D Host gibi adlarla geçen modellerden biriyle yapılır.

Bu rehberin ilk yarısı işletme sahipleri için: para akışı, valör ve bloke, banka ile ödeme kuruluşu karşılaştırması ve başvuru belgeleri. İkinci yarısı geliştiriciler için: 3D Secure akışı, entegrasyon modelleri, hash hesaplama, dönüşün doğrulanması, test, iptal ve iade, güvenlik ve sorun giderme. Parametre adları, hash sürümleri ve durum kodları bankadan bankaya değişir. Örneklerimiz gerçek altyapılara dayanıyor ve çoğu açık kaynak mews/pos kütüphanesinin kaynak kodundan alındı; yine de kendi bankanızın güncel entegrasyon dokümanıyla doğrulamadan kullanmayın.

Sanal POS nedir, para nasıl akar?​

Kartlı ödemede dört taraf vardır: kart hamili (cardholder), kartı çıkaran ihraççı banka (issuer), satışı yapan üye işyeri (merchant) ve ödemeyi üye işyeri adına kabul eden kuruluş (acquirer). Fiziksel POS'ta kart cihaza takılır ya da yaklaştırılır. Sanal POS'ta ise kart bilgisi bir web formuna veya bankanın ödeme sayfasına girilir; işlem, kart fiziksel olarak ibraz edilmeden (card-not-present) gerçekleşir.

Türkiye'de kabul tarafında iki tür kuruluş bulunur. Bankalar sanal POS'u kendi üye işyeri sözleşmeleriyle verir. Ödeme kuruluşları ve elektronik para kuruluşları ise 6493 sayılı Ödeme ve Menkul Kıymet Mutabakat Sistemleri, Ödeme Hizmetleri ve Elektronik Para Kuruluşları Hakkında Kanun kapsamında çalışır; kanunun 12. maddesi ödeme aracının kabulünü de ödeme hizmetleri arasında sayar. Bu kuruluşlar faaliyet iznini Türkiye Cumhuriyet Merkez Bankası'ndan (TCMB) alır. Yetki, 2019'da 7192 sayılı Kanunla yapılan değişiklikle BDDK'dan TCMB'ye geçmiştir. Örneğin iyzico ve PayTR, TCMB'nin elektronik para kuruluşları listesinde yer alır. Sözleşme imzalamadan önce sağlayıcınızın bu listede ya da ödeme kuruluşları listesinde bulunduğunu kontrol edin.

Bir kart ödemesinde para şu yolu izler:
  1. Müşteri ödemeyi onaylar. Banka ya da ödeme kuruluşu, kart şeması üzerinden ihraççı bankadan provizyon (authorization) ister; yurt içi işlemlerin yönlendirme ve takasında BKM'nin (Bankalararası Kart Merkezi) altyapısı da rol oynar. Onaylanan tutar kartın kullanılabilir limitinden düşülür, ama henüz size geçmez.
  2. Gün sonunda (end-of-day, batch) o günün işlemleri kapatılır ve takasa gönderilir. Gün sonu saatini kuruluş belirler; ileride göreceğimiz iptal ve iade ayrımı da bu ana bağlıdır.
  3. Tutar, komisyon düşüldükten sonra sözleşmedeki takvime göre hesabınıza geçer.

Valör (value date), paranın hesabınızda kullanılabilir hâle geldiği tarihtir. Bloke ise tutarın, sözleşmede belirlenen süre boyunca blokeli bir hesapta bekletilmesidir; birçok sözleşmede bloke süresi uzadıkça komisyon oranı düşer. Taksitli satışlarda tutar size ya tek seferde, çoğunlukla daha yüksek bir komisyonla, ya da taksit vadelerine yayılarak ödenir. Komisyon oranı, bloke süresi, taksitli işlemlerin ödeme biçimi ve ters ibraz (chargeback) riskine karşı istenebilecek teminat, pazarlıkla belirlenen sözleşme maddeleridir. Bu yüzden burada oran vermiyoruz. Teklif alırken her kart tipi ve taksit sayısı için ayrı oranı, bloke süresini ve hesaba geçiş takvimini yazılı olarak isteyin.

Banka sanal POS'u mu, ödeme kuruluşu mu?​

ÖlçütBanka sanal POS'uÖdeme veya elektronik para kuruluşu
BaşvuruŞube ya da kurumsal satış ekibi üzerinden; risk değerlendirmesi, site incelemesi, bazı durumlarda teminatÇoğunlukla çevrim içi başvuru ve daha hızlı onay; belge kontrolü yine yapılır
FiyatlandırmaHacme, kart tipine, taksit sayısına ve bankayla genel ilişkinize göre pazarlıkYayımlanmış standart tarife; yüksek hacimde pazarlık payı
Hesaba geçişÇoğu zaman aynı bankadaki hesabınıza; valör ve bloke sözleşmeyle belirlenirTanımladığınız banka hesabına; takvim ve olası bloke sözleşmeyle belirlenir
Taksit (installment)Genellikle bankanın kendi kart programındaki kartlara (ör. Garanti BBVA'da Bonus, Yapı Kredi'de World, İş Bankası'nda Maximum, Akbank'ta Axess)Tek entegrasyonla çok sayıda kart programına taksit
Birden fazla bankaHer banka için ayrı sözleşme ve entegrasyon; kartları hangi POS'a yönlendireceğinizi siz kurgularsınızSağlayıcı arka planda yönetir
EntegrasyonHer bankanın ya da POS altyapısının kendi alanları, hash kuralı ve test süreci vardırTek API veya SDK; yaygın e-ticaret platformları için hazır eklentiler
Pazaryeri, alt üye işyeriKüçük işletmelere seyrek sunulur; ayrıca görüşülürAlt üye işyeri ve bölünmüş ödeme (split payment) birçok sağlayıcıda ürün olarak sunulur
DestekBankanın POS destek hattı ve entegrasyon ekibiÜye işyeri paneli, geliştirici dokümanı, test ortamı ve destek kanalları
PCI DSS kapsamıSeçilen entegrasyon modeline bağlıdır; bankanın ortak ödeme sayfasıyla küçülürBarındırılan form ya da iframe ile genellikle en küçük kapsam

Yeni ve küçük bir mağaza için ödeme kuruluşu çoğu zaman en hızlı yoldur: tek entegrasyon, kart programlarının çoğuna taksit ve kurulmaya hazır eklentiler. Ciro büyüdükçe toplam maliyet öne çıkar. Müşterilerinizin önemli bir kısmı belirli bir kart programını kullanıyorsa, o programın bankasından sanal POS alıp diğer kartlar için ödeme kuruluşunu tutmak yaygın bir karma modeldir. Kararı yalnızca manşet komisyon oranına bakarak vermeyin; bloke süresini, iade ve ters ibraz süreçlerini ve iki ayrı kaynağın mutabakatını yürütmenin yükünü de hesaba katın. Peşin fiyatına taksit sunarsanız taksit maliyeti size kalır; vade farkını müşteriye yansıtıyorsanız toplam tutarı ödeme adımında açıkça gösterin. Sağlayıcıların özellik ve ücret karşılaştırmasını ödeme sağlayıcısı seçimi konumuzda ele aldık.

Sanal POS nasıl alınır: başvuru ve belgeler​

Belge listesi kuruluşa göre değişir; en sık istenenler şunlardır:
  • Vergi kaydı olan bir işletme (şahıs şirketi, limited ya da anonim şirket), vergi levhası ve imza sirküleri (şahıs işletmelerinde imza beyannamesi); şirketler için ticaret sicil gazetesi ve faaliyet belgesi; yetkililerin kimlik bilgileri.
  • Ödemelerin aktarılacağı ticari hesap (IBAN). Bankalar çoğunlukla hesabın kendi nezdinde açılmasını ister.
  • Yayında olan ve HTTPS ile çalışan bir site; ürünlerin, fiyatların ve iletişim bilgilerinin (unvan, adres, telefon, e-posta) açıkça göründüğü sayfalar.
  • Yasal metinler: mesafeli satış sözleşmesi, ön bilgilendirme formu, cayma, iptal ve iade koşulları, gizlilik politikası ve KVKK aydınlatma metni.
  • Kendi sitesinden satış yapan işletmeler için ETBİS (Elektronik Ticaret Bilgi Sistemi) kaydı.
  • Faaliyet alanı, ortalama sepet tutarı ve beklenen aylık hacim. Bazı sektörler ek inceleme ya da teminat gerektirebilir, bazıları hiç kabul edilmeyebilir.
Onaydan sonra size üye işyeri numarası (merchant ID), API kullanıcı adı ve şifresi, 3D işlemleri için mağaza anahtarı (store key) ve test ortamı bilgileri verilir. Canlı ortam bilgileriyle tahsilat ve iade yapılabildiği için bunları kaynak kod deposuna değil, ortam değişkenlerine ya da bir gizli bilgi yöneticisine (secret manager) koyun.

3D Secure nedir, ödeme akışı nasıl işler?​

3D Secure, kart hamilinin kimliğinin ödeme anında ihraççı banka tarafından doğrulandığı protokoldür. Adındaki "3D" üç alanı (three domains) anlatır: üye işyeri ile acquirer'ın bulunduğu taraf (acquirer domain), kart şemalarının işlettiği ara katman (interoperability domain) ve ihraççı bankanın tarafı (issuer domain). Bugün kullanılan sürüm EMVCo'nun yayımladığı EMV 3-D Secure, yani 3DS 2.x'tir. EMVCo'nun sayfasındaki güncel belirtim bültenleri 2.2.0 ile 2.3.1.1 arasındaki sürümleri kapsıyor ve kurum 2.2 ya da üstünü öneriyor; 2.4.0 ise Haziran 2026'da taslak olarak görüşe açıldı. Büyük kart şemaları eski 3DS 1 protokolünü artık desteklemiyor.

Akıştaki bileşenler şunlardır:
  • 3DS Requestor: Doğrulamayı başlatan taraf, yani üye işyeri.
  • 3DS Server: Doğrulama isteğini oluşturan sunucu. Türk bankalarının sanal POS'larında bu rolü genellikle bankanın 3D ödeme geçidi (3D gateway) üstlenir; siz tarayıcıyı bir formla bu geçide gönderirsiniz.
  • Directory Server (DS): Visa ve Mastercard gibi kart şemalarının işlettiği, isteği kartın ihraççı bankasına yönlendiren dizin sunucusu.
  • ACS (Access Control Server): İhraççı bankanın kart hamilini doğrulayan sunucusu. SMS ile gelen tek kullanımlık şifre ekranı ya da mobil bankacılık uygulamasından gelen onay isteği buradan çıkar.

Akış dört adımda özetlenebilir:
  1. Müşteri kart bilgisini girer. 3DS Server, kart ve işlem bilgilerini (tutar, para birimi, tarayıcı ve cihaz bilgileri) DS üzerinden ACS'ye iletir.
  2. ACS bir risk değerlendirmesi yapar. Riski düşük bulursa müşteriye bir şey sormadan doğrular; buna sürtünmesiz (frictionless) akış denir. Aksi hâlde müşteriden şifre ya da uygulama onayı ister (challenge flow). Türkiye'de kullanıcıların alıştığı ekran, SMS ile gelen şifrenin girildiği bu adımdır.
  3. Sonuç, kriptografik bir doğrulama değeri (CAVV ya da AAV) ve ECI (Electronic Commerce Indicator) ile birlikte döner.
  4. Ödeme ancak bundan sonra yetkilendirilir: provizyon isteği bu doğrulama verileriyle ihraççı bankaya gider; banka limite, kartın durumuna ve kendi kurallarına göre onaylar ya da reddeder.

En sık yapılan kavram hatası, başarılı bir 3D doğrulamasını başarılı ödeme sanmaktır. Doğrulama kart hamilinin kimliğini kanıtlar; provizyon ise paranın çekilip çekilemeyeceğine karar verir. 3D adımını geçip yetersiz bakiye nedeniyle reddedilen bir işlem tamamen olağandır. 3DS 2 ayrıca mobil uygulamalar için bir SDK tanımlar; böylece doğrulama tarayıcıya çıkmadan uygulamanın içinde yapılabilir.

ECI değeri kart şemasına göre değişir. Örneğin Visa'da 05 tam doğrulamayı, 06 doğrulama denemesini; Mastercard'da 02 tam doğrulamayı, 01 denemeyi gösterir. Dönüşte gelen ECI ve CAVV değerlerini işlem kaydınızla birlikte saklayın; ters ibraza itiraz ederken kanıt olarak işinize yarar.

Sorumluluk kayması (liability shift): Başarıyla doğrulanmış bir işleme kart hamili "bu harcamayı ben yapmadım" diyerek dolandırıcılık gerekçeli itiraz açarsa, kayıp riski genellikle üye işyerinden ihraççı bankaya geçer. Bu bir garanti değildir; kapsamını kart şemasının kuralları, ECI değeri ve sözleşmeniz belirler. "Ürün gelmedi" ya da "ürün açıklamaya uymuyor" gibi dolandırıcılık dışı itirazları ise hiç kapsamaz; bu itirazlarda kargo ve teslimat kanıtınız belirleyicidir.

Entegrasyon modelleri: NonSecure, 3D, 3D Pay, 3D Host​

NonSecure (2D, 3D'siz): Kart bilgileri sizin sunucunuzdan bankanın API'sine tek bir istekle gönderilir; 3D doğrulaması yapılmaz. Bankalar bu modeli genellikle yalnızca özel izinle açar ve sahtekârlık kaynaklı itirazların yükü üye işyerinde kalır.

3D (3D Secure, 3D Model): Kart formu sizin sayfanızdadır; tarayıcı, kart bilgileriyle birlikte bankanın 3D geçidine gönderilir. Banka yalnızca doğrulamayı yapar ve sonucu dönüş adresinize (callback URL) iletir. Tahsilat için sunucunuz ayrıca bir provizyon isteği gönderir. İki adımlı yapı, doğrulama sonucunu gördükten sonra ve provizyondan önce stok ya da sahtekârlık kontrollerinizi çalıştırmanıza imkân verir.

3D Pay: Form yine sizin sayfanızdadır, ancak banka doğrulamanın ardından provizyonu da kendisi yapar. Dönüşte ödemenin sonucunu alırsınız; ayrı bir provizyon çağrısı yoktur.

3D Host ve 3D Pay Hosting (ortak ödeme sayfası): Müşteri kart bilgisini bankanın barındırdığı sayfada girer; sizin sayfanız kart verisini hiç görmez. NestPay'deki 3D Pay Hosting'de banka bu sayfadan sonra provizyonu da yapar; mews/pos, NestPay'in 3D Host dönüşünü de ayrı bir provizyon çağrısı olmadan sonuçlandırır. Ortak ödeme sayfası varyantları altyapıya göre değiştiği için dönüşün kesinleşmiş bir ödemeyi mi yoksa yalnızca doğrulama sonucunu mu bildirdiğini dokümandan teyit edin. Ödeme kuruluşlarının barındırılan formları, örneğin iyzico Checkout Form ya da PayTR iFrame API, bu modelin karşılığıdır.

ModelKart verisini kim toplar?Tipik PCI DSS SAQSunucudan bankaya çağrıKullanıcı deneyimi
NonSecureSizin sunucunuzSAQ D1 ödeme isteğiYönlendirme yok; risk en yüksek
3DSizin sayfanızForm doğrudan bankaya gidiyorsa SAQ A-EP, kart sunucunuzdan geçiyorsa SAQ DDoğrulamadan sonra 1 provizyon isteğiKendi formunuz, ardından bankanın doğrulama ekranı
3D PaySizin sayfanız3D ile aynıYok; gerektiğinde durum sorgusu3D ile aynı
3D Host, 3D Pay HostingBankanın sayfasıGenellikle SAQ ANestPay'de yok; diğer altyapılarda dokümandan teyit edinBanka sayfasına geçiş; tasarım seçenekleri sınırlı

Bu adlar standart değildir, POS altyapısına göre değişir. mews/pos belgelerine göre aralarında İş Bankası, Halkbank, Ziraat Bankası ve TEB'in de bulunduğu bankaların kullandığı Asseco (Payten) NestPay altyapısında model, storetype alanında 3d, 3d_pay, 3d_pay_hosting ya da 3d_host olarak gönderilir. Garanti BBVA'da aynı bilgi secure3dsecuritylevel alanında 3D ya da 3D_PAY gibi değerlerle taşınır. Yapı Kredi'nin PosNet'i, VakıfBank'ın da kullandığı PayFlex ve Kuveyt Türk'ün sanal POS'u gibi bazı altyapılarda ise 3D formunu oluşturmadan önce sunucunuzun bankaya bir API çağrısı yapması gerekir. Açık kaynak PHP kütüphanesi mews/pos bu farkları tek bir arayüzde toplar; 2.0.0 sürümü Temmuz 2026'da yayımlandı ve PHP 8.0 ya da üstünü gerektirir. Kütüphane kullansanız bile bankanın dokümanını okuyun: Hangi modellerin desteklendiği ve dönüş kodlarının ne anlama geldiği orada yazar. Sözleşmenizde hangi modelin açık olduğunu ise bankanıza teyit ettirin.

Adım adım sanal POS entegrasyonu​

Aşağıdaki akış üç 3D modelinin ortak iskeletidir; farkları her adımda belirttik.

Code:
Tarayıcı --(form POST: sipariş, tutar, hash)--> Bankanın 3D geçidi
3D geçidi --> Directory Server --> ACS (SMS şifresi ya da uygulama onayı)
ACS --> 3D geçidi: doğrulama sonucu (mdStatus, ECI, CAVV)
(3D Pay ve 3D Pay Hosting) 3D geçidi provizyonu kendisi yapar
3D geçidi --(tarayıcı üzerinden POST: sonuç ve hash)--> okUrl / failUrl
(3D) Sunucunuz --(provizyon isteği: md, xid, eci, cavv)--> Bankanın API'si

1. Hazırlık​

Bankadan şunları alın: test ve canlı 3D geçidi adresleri, API adresi, üye işyeri numarası, API kullanıcısı ve şifresi, mağaza anahtarı, test kartları ve sözleşmenizde açık olan model. Bazı bankalar API çağrılarını yalnızca tanımlı IP adreslerinden kabul eder. Paylaşımlı hostingde sunucunun dışarı çıkan IP adresi, panelde gördüğünüz adresten farklı olabilir; çıkış IP'nizi doğrulayıp bankaya bildirin.

2. İsteği oluşturun​

  • Sipariş numarası: Her ödeme denemesi için benzersiz bir numara üretin ve siparişinize bağlayın. Aynı numaranın yeniden kullanılmasına ilişkin kurallar altyapıya göre değişir; müşteri ilk denemede başarısız olup tekrar denediğinde yeni numara kullanmak bu belirsizliği ortadan kaldırır.
  • Tutar: Tutarı veritabanında kuruş cinsinden tam sayı olarak tutun ve dokümandaki biçime yalnızca gönderirken çevirin. mews/pos kaynak koduna göre NestPay tabanlı altyapılara tutar, ondalık ayırıcısı nokta olan bir sayı olarak gönderilir (kütüphane 149,90 TL'yi 149.9 olarak yazar); Garanti BBVA ve PosNet ise kuruş cinsinden tam sayı (14990) bekler. Yerel ayara bağlı biçimlendirme (1.234,50) kullanmayın. Hash'e giren tutar metni, formda gönderilen metinle birebir aynı olmalıdır.
  • Para birimi: Çoğu altyapı ISO 4217 sayısal kodunu kullanır: Türk lirası için 949, ABD doları için 840, avro için 978. PosNet ise mews/pos'a göre TL, US ve EU gibi harf kodları bekler.
  • Taksit: mews/pos'ta tek çekim NestPay'e boş değerle, PosNet'e 00 ile gönderilir; taksitli işlemde taksit sayısı yazılır. Taksit yalnızca kredi kartlarında geçerlidir; banka kartları (debit) ve yurt dışında çıkarılmış kartlar genellikle tek çekim işlenir. Bazı sektörlerde taksit sayısı düzenlemelerle sınırlandırılmıştır ve bu sınırlar zaman zaman değişir; ürün grubunuz için güncel sınırı bankanıza sorun.
  • Dönüş adresleri: Başarılı ve başarısız sonuç için HTTPS adresleri (okUrl ve failUrl gibi). Bu adresler yönlendirme yapmamalıdır. Bazı altyapılar tek bir adres kabul ettiği için ikisini aynı tutmak işinizi kolaylaştırır.
  • Diğer alanlar: Rastgele bir değer, dil, işlem tipi ve model. Parametre adları da değişir: NestPay'de clientid, oid, amount, currency, taksit, okUrl, failUrl, rnd, storetype ve hashAlgorithm kullanılırken Garanti BBVA'da orderid, txnamount, txncurrencycode, txninstallmentcount, successurl ve errorurl kullanılır.

3. Hash'i hesaplayın​

Hash, isteğin sizden geldiğini ve yolda değiştirilmediğini kanıtlar: Tarayıcıda tutarı değiştirilmiş bir istek hash'le uyuşmaz ve banka tarafından reddedilir. Yalnızca sizin ve bankanın bildiği mağaza anahtarıyla hesaplanır ve anahtar hiçbir zaman tarayıcıya gönderilmez. Kurallar altyapıya göre belirgin biçimde farklıdır:
  • NestPay, hashAlgorithm=ver3 (mews/pos uygulaması): hash ve encoding dışındaki tüm alanlar, büyük/küçük harf ayrımı yapılmadan ada göre sıralanır. Değerlerdeki ters bölü (\) ve dikey çizgi (|) karakterlerinin önüne ters bölü eklenir, değerler dikey çizgiyle birleştirilir ve sona mağaza anahtarı eklenir. Ortaya çıkan metnin SHA-512 özeti Base64 ile kodlanır.
  • Garanti BBVA (mews/pos uygulaması): Belirli alanlar sabit bir sırayla ve ayraçsız birleştirilir. Buna mağaza anahtarı ile provizyon şifresinden ve terminal numarasından SHA-1 ile türetilen bir güvenlik değeri eklenir; sonuç büyük harfli onaltılık (hex) bir SHA-512 özetidir. Dönüşte hangi alanların hash'e girdiği hashparams alanında listelenir.
Aynı altyapıda birden fazla hash sürümü bulunabilir. İstekte hangi sürümü bildirdiyseniz dönüşü de o sürümün kuralıyla doğrulayın.

4. Tarayıcıyı bankaya gönderin​

Sunucunuz, alanları ve hash'i gizli alanlar (hidden input) olarak içeren bir HTML formu üretir; form POST ile bankanın 3D geçidine gider. 3D Host'ta formda kart alanı bulunmaz ve form, sayfa açılır açılmaz kendiliğinden gönderilebilir (auto-submit). 3D ve 3D Pay'de ise iki yol vardır: Kart alanları müşterinin doldurduğu ve doğrudan bankaya gönderilen formda yer alır ya da kartı önce sunucunuz alır ve formu kendisi üretir. İkinci yol PCI DSS kapsamınızı SAQ D'ye büyütür; bunu bilerek seçin.

5. Dönüşü doğrulayın ve siparişi güncelleyin​

Banka sonucu, dönüş adresinize müşterinin tarayıcısı üzerinden POST eder. İstek tarayıcıdan geçtiği için alanları değiştirilmiş olabilir; her alanı güvenilmeyen girdi olarak ele alın:
  1. Hash'i dokümandaki dönüş kuralıyla yeniden hesaplayın ve PHP'deki hash_equals gibi sabit zamanlı bir karşılaştırmayla doğrulayın.
  2. Deneme kaydını sipariş numarasıyla bulun. Durumu beklemede değilse işlem zaten sonuçlanmıştır; aynı işlemi ikinci kez yapmayın.
  3. Dönen tutar ve para biriminin kayıttakiyle aynı olduğunu kontrol edin.
  4. Doğrulama durumunu kontrol edin. NestPay'de bu alanın adı mdStatus, Garanti BBVA'da mdstatus'tur. mews/pos, NestPay için yalnızca 1 değerini başarılı sayarken Garanti BBVA için 1 ile 4 arasını kabul eder; hangi değerleri kabul edeceğinizi bankanızın dokümanı ve sözleşmeniz belirler.
  5. 3D modelinde provizyonu şimdi yapın: NestPay'de dönüşteki md, xid, eci ve cavv değerleri provizyon isteğine sırasıyla Number, PayerTxnId, PayerSecurityLevel ve PayerAuthenticationCode olarak eklenir. 3D Pay ve 3D Pay Hosting'de ödeme sonucu zaten dönüştedir; NestPay'de onay için ProcReturnCode=00 ve mdStatus=1 birlikte aranır.
  6. Deneme ve sipariş kayıtlarını aynı veritabanı işleminde (transaction) ve yalnızca durum hâlâ beklemedeyse güncelleyin. Ardından 303 See Other ile bir sonuç sayfasına yönlendirin (Post/Redirect/Get); müşteri sayfayı yenilediğinde POST tekrarlanmaz.

Dönüş hiç gelmeyebilir: Müşteri doğrulama ekranında sekmeyi kapatabilir ya da bağlantısı kopabilir. Belirli bir süredir beklemede olan denemeleri bankanın durum sorgusu servisiyle (mews/pos'a göre NestPay'de ORDERSTATUS, Garanti BBVA'da orderinq) tamamlayan zamanlanmış bir görev çalıştırın. Ayrıca her gün kendi kayıtlarınızı bankanın işlem listesiyle karşılaştırın. Bazı sağlayıcılar ayrıca sunucudan sunucuya bildirim gönderir; böyle bir bildirim alıcısında imza doğrulama, tekrar deneme (retry) ve denetim kaydı tasarımı için webhook alıcıları konumuza bakabilirsiniz.

Aşağıdaki PHP iskeleti bu adımları gösterir. Parametre adları, adresler ve durum kodları bilerek genel tutuldu; hiçbir bankanın gerçek belirtimi değildir.

PHP:
<?php
// YALNIZCA İSKELET: Parametre adları, adresler, durum kodları ve hash kuralı yer tutucudur,
// hiçbir bankanın belirtimi değildir. Gerçek değerleri bankanızın dokümanından alın.
declare(strict_types=1);

const GATEWAY_URL = 'https://3d-gateway.bank.example/pay';   // dokümandaki adres
const RETURN_URL  = 'https://shop.example.com/payment/return';

function storeKey(): string
{
    $key = getenv('POS_STORE_KEY');   // gizli bilgi deposundan; tarayıcıya asla gitmez
    if ($key === false || $key === '') {
        throw new RuntimeException('POS_STORE_KEY tanımlı değil');
    }
    return $key;
}

// Belgelenmiş bir örnek (mews/pos'taki NestPay ver3 uygulaması): hash ve encoding dışındaki
// alanları ada göre sırala, \ ve | karakterlerinin önüne \ ekle, | ile birleştir,
// mağaza anahtarını ekle, SHA-512, Base64.
function bankHash(array $fields): string
{
    foreach (array_keys($fields) as $name) {
        if (in_array(strtolower((string) $name), ['hash', 'encoding'], true)) {
            unset($fields[$name]);   // dönüşte HASH gibi büyük harfle de gelebilir
        }
    }
    ksort($fields, SORT_NATURAL | SORT_FLAG_CASE);
    $values   = array_values($fields);
    $values[] = storeKey();
    $escaped  = array_map(
        fn ($v): string => str_replace(['\\', '|'], ['\\\\', '\\|'], (string) $v),
        $values
    );
    return base64_encode(hash('sha512', implode('|', $escaped), true));
}

function startPayment(PDO $db, int $orderId, int $amountKurus, int $installments): string
{
    $attemptId = $orderId . '-' . bin2hex(random_bytes(4));   // her deneme için yeni numara
    $db->prepare("INSERT INTO payment_attempts (id, order_id, amount_kurus, status)
                  VALUES (?, ?, ?, 'pending')")
       ->execute([$attemptId, $orderId, $amountKurus]);

    $fields = [
        'merchant_id' => (string) getenv('POS_MERCHANT_ID'),
        'order_id'    => $attemptId,
        'amount'      => number_format($amountKurus / 100, 2, '.', ''),  // bazı altyapılar kuruş ister
        'currency'    => '949',                                          // TL için ISO 4217 sayısal kodu
        'installment' => $installments > 1 ? (string) $installments : '',
        'ok_url'      => RETURN_URL,
        'fail_url'    => RETURN_URL,
        'model'       => '3d_pay_hosting',                               // model adı dokümana göre
        'random'      => bin2hex(random_bytes(10)),
    ];
    $fields['hash'] = bankHash($fields);

    $inputs = '';
    foreach ($fields as $name => $value) {
        $inputs .= sprintf('<input type="hidden" name="%s" value="%s">',
            htmlspecialchars($name), htmlspecialchars($value));
    }
    return '<form id="pay" method="post" action="' . htmlspecialchars(GATEWAY_URL) . '">'
        . $inputs . '</form><script>document.getElementById("pay").submit();</script>';
}

function handleReturn(PDO $db, array $post): void
{
    $attemptId = (string) ($post['order_id'] ?? '');
    if (!hash_equals(bankHash($post), (string) ($post['hash'] ?? ''))) {
        error_log("payment return: hash mismatch, attempt {$attemptId}");   // kart verisini asla loglamayın
        http_response_code(400);
        return;
    }

    $db->beginTransaction();
    $stmt = $db->prepare('SELECT amount_kurus, status FROM payment_attempts WHERE id = ? FOR UPDATE');
    $stmt->execute([$attemptId]);
    $attempt = $stmt->fetch(PDO::FETCH_ASSOC);

    if ($attempt !== false && $attempt['status'] === 'pending') {
        $amountOk   = (int) round((float) ($post['amount'] ?? 0) * 100) === (int) $attempt['amount_kurus'];
        $currencyOk = ($post['currency'] ?? '') === '949';
        $authOk     = ($post['auth_status'] ?? '') === '1';      // ör. mdStatus; anlamı bankaya göre değişir
        $paidOk     = ($post['response_code'] ?? '') === '00';   // 3D Pay veya ortak sayfa: onay kodu
        // 3D modelinde bunun yerine burada, bu dönüşteki doğrulama verileriyle provizyon
        // API'sini çağırın ve $paidOk değerini onun yanıtından alın.

        $status = ($amountOk && $currencyOk && $authOk && $paidOk) ? 'paid' : 'failed';
        $db->prepare("UPDATE payment_attempts SET status = ?, bank_ref = ?
                      WHERE id = ? AND status = 'pending'")
           ->execute([$status, $post['bank_ref'] ?? null, $attemptId]);
        // Ödeme başarılıysa siparişi de aynı veritabanı işlemi içinde ödendi olarak işaretleyin.
    }
    $db->commit();

    header('Location: /payment/result?attempt=' . rawurlencode($attemptId), true, 303);
}

Test, iptal ve iade​

Test ortamının adresini, test üye işyeri bilgilerini, test kartlarını ve doğrulama şifresini bankanızın dokümanından alın; test ortamında gerçek kart kullanmayın. Canlıya geçmeden önce en az şu senaryoları deneyin:
  • Başarılı tek çekim ve taksitli ödeme; kuruşlu bir tutar (ör. 1,01 TL).
  • Yanlış şifre, doğrulama ekranında iptal ve doğrulama ekranında sekmenin kapatılması.
  • Limit ya da bakiye yetersizliği nedeniyle red (dokümanda böyle bir test kartı varsa).
  • Dönüş sayfasının yenilenmesi, geri tuşu ve ödeme düğmesine çift tıklama.
  • Aynı gün iptal; gün sonundan sonra tam ve kısmi iade.
  • Hash'i bozulmuş bir dönüşün reddedilmesi.

İşlemNe zaman kullanılır?Kart hamili ne görür?İşlem tipi (NestPay / Garanti BBVA)
SatışÖdeme anında; provizyon ve tahsilat birlikteHarcamaAuth / sales
Ön provizyon (pre-authorization)Tutarı ayırıp sonra kesinleştirmek için: otel, araç kiralama, stok teyidiKart limitinde blokePreAuth / preauth
Provizyon kapama (post-authorization)Ön provizyonu kesin satışa çevirmek içinHarcamaPostAuth / postauth
İptal (void)Gün sonundan önce; genellikle yalnızca tam tutarBloke kalkar; ekstrede çoğunlukla görünmezVoid / void
İade (refund)Gün sonundan sonra; tam ya da kısmi tutarAyrı bir iade satırı; yansıma süresi ihraççı bankaya bağlıCredit / refund
İşlem tipi adları mews/pos kaynak kodundan alındı. Gün sonundan önce kısmi tutar iade edilip edilemeyeceğini ve iade için ayrı bir kullanıcı ya da şifre gerekip gerekmediğini dokümandan teyit edin; örneğin mews/pos, Garanti BBVA'da iptal ve iade isteklerini ayrı bir iade şifresiyle imzalar. Sipariş sisteminiz, bankadaki iade işleminin başarıyla döndüğünü görmeden müşteriye "iadeniz yapıldı" bildirimi göndermemelidir.

Güvenlik ve PCI DSS uyumu​

PCI DSS, kart verisini işleyen, saklayan ya da ileten herkesten kart şemalarının beklediği güvenlik standardıdır. PCI SSC'nin belge kütüphanesinde güncel sürüm olarak PCI DSS v4.0.1 yer alıyor. Hangi öz değerlendirme anketini (SAQ) dolduracağınızı ve beyanı ne sıklıkla vereceğinizi üye işyeri bankanız ya da ödeme kuruluşunuz belirler. Pratikte şu kurallar geçerlidir:
  • Kart numarasını (PAN) ve güvenlik kodunu (CVV2/CVC2) saklamayın. PCI DSS, güvenlik kodunun yetkilendirmeden sonra şifrelenmiş olarak bile saklanmasına izin vermez. Kayıtlı kartla ödeme (card-on-file) gerekiyorsa sağlayıcınızın kart saklama (tokenization) hizmetini kullanın.
  • Mümkünse ortak ödeme sayfası ya da barındırılan form kullanın; kapsamı en çok küçülten karar budur. Yine de SAQ A ile yükümlülük bitmez: PCI SSC'nin Haziran 2026 tarihli SSS 1604 yanıtına göre müşteriyi yönlendiren ya da iframe gömen sayfalar için ASV dış zafiyet taramaları SAQ A kapsamındadır. Ödeme formunu iframe ile gömüyorsanız, Şubat 2025 tarihli SSS 1588 uyarınca sayfanızın script saldırılarına karşı korunduğunu da teyit etmeniz gerekir.
  • Kart formu sizin sayfanızdaysa PCI DSS v4'ün 6.4.3 (ödeme sayfasındaki scriptlerin envanteri ve yetkilendirilmesi) ve 11.6.1 (değişiklik ve kurcalama tespiti) gerekliliklerine dikkat edin; analitik, canlı sohbet ve reklam scriptlerini ödeme sayfasından çıkarın.
  • Günlüklere (log) ham form gövdesini yazmayın. Kart numarası, güvenlik kodu ve son kullanma tarihi alanlarını filtreleyin; deneme numarası, bankanın döndürdüğü maskeli kart numarası ve sonuç kodları yeterlidir.
  • TLS 1.2 ya da üstünü kullanın, mağaza anahtarını gizli bilgi yöneticisinde tutun ve sızdığından şüphelenirseniz bankadan yenisini isteyin. Üye işyeri paneline erişimi kişiye özel hesaplarla ve iki adımlı doğrulamayla sınırlayın.

Sahtekârlığa karşı temel kurallar:
  • Hız kuralları (velocity): Aynı IP adresinden, karttan, cihazdan ya da hesaptan kısa sürede gelen deneme sayısını sınırlayın.
  • Kart test saldırıları (card testing): Küçük tutarlı, art arda başarısız denemeler bu saldırının tipik izidir. Hız sınırı, CAPTCHA ve geçici engelleme uygulayın.
  • Kartın BIN'inden (ilk 6 ya da 8 hanesi) çıkan ülke ile IP adresinin ve teslimat adresinin ülkesi arasındaki uyumsuzluğu puanlayın.
  • Tutar eşikleri koyun. İlk siparişte yüksek tutar ya da aynı hesapta çok sayıda farklı kart gibi durumları elle inceleme kuyruğuna alın.
3D Secure, çalıntı kart bilgileriyle yapılan ödemelere karşı güçlü bir engeldir; ancak hesabın ele geçirilmesini ya da kart hamilinin kendi alışverişine itiraz etmesini (friendly fraud) çözmez.

Sanal POS hash hatası ve diğer sık sorunlar​

BelirtiOlası nedenNe yapmalı?
İstekte "hash hatası"Alan sırası yanlış ya da bir alan eksik; hash sürümü uyuşmuyor; hash'teki tutar metni formdakinden farklı (149.9 ile 149.90); test ve canlı mağaza anahtarları karışmış; fazladan boşluk ya da karakter kodlamasıHash girdisini test ortamında loglayın, ama mağaza anahtarını ve kart alanlarını maskeleyin (mews/pos'un NestPay uygulamasında kart sunucunuzdan geçiyorsa kart numarası ve CVV de hash'e girer). Alan sırasını ve biçimini dokümandaki örnek hesapla karşılaştırın, alanları tek tek ekleyerek deneyin
Dönüşteki hash doğrulanmıyorBanka hash'e girmeyen bir alan eklemiş (mews/pos, Ziraat dönüşlerinde görülebilen nationalidno alanını Asseco'nun teyidiyle hash dışında tutar); banka alanları hashparams ile listelerken tüm alanları hash'lemek ya da tersi; Türkçe karakter içeren ad ve adres alanlarında karakter kodlamasıDokümandaki dönüş kuralını birebir uygulayın; encoding alanını kontrol edin
mdStatus 1 değilKart 3D'ye kayıtlı değil, şifre yanlış girildi, ACS hata verdi ya da müşteri vazgeçtimdErrorMsg alanına ve bankanın kod tablosuna bakın; 2 ile 4 arasındaki değerlerle devam edilip edilemeyeceği sözleşme konusudur
Dönüş adresine istek hiç gelmiyorokUrl'de 301/302 yönlendirmesi var (tarayıcı POST'u GET'e çevirir ve gövde kaybolur); WAF ya da bot koruması POST'u engelliyor; sertifika hatası; müşteri sekmeyi kapattıYönlendirmesiz son adresi verin, WAF'ta istisna tanımlayın, eksik kalanları durum sorgusuyla tamamlayın
Dönüşte oturum düşüyor, sepet boş görünüyorOturum çerezi SameSite=Lax (açıkça ya da tarayıcının varsayılanı olarak) ve siteler arası POST ile gönderilmiyorDönüşü oturumsuz tasarlayın (kaydı deneme numarasıyla bulun) ya da oturum çerezini SameSite=None; Secure yapın
419 Page Expired (Laravel) ya da başka bir CSRF hatasıFramework'ün CSRF koruması bankadan gelen POST'u reddediyorDönüş rotasını CSRF doğrulamasından muaf tutun, hash doğrulamasını zorunlu tutun
Tutar ya da para birimi hatasıYerel ayar biçimi (1.234,50), kayan nokta yuvarlaması, 949 yerine TRY gönderilmesi, lira ile kuruşun karışmasıTutarı kuruş cinsinden tam sayı tutun, dokümandaki biçimle üretin
Mükerrer sipariş ya da çift tahsilat şüphesiSayfa yenileme, çift tıklama, dönüş ve bildirimin aynı anda gelmesiKoşullu güncelleme (WHERE status = 'pending'), 303 ile Post/Redirect/Get, her deneme için ayrı numara
Provizyon isteği zaman aşımına uğruyorAğ sorunu; banka yanıt vermiş ama yanıt size ulaşmamış olabilirKörlemesine tekrar denemeyin; önce durum sorgusu yapın
API çağrıları reddediliyor ya da bağlantı kurulamıyorSunucunun çıkış IP'si bankada tanımlı değil (paylaşımlı hostingde paneldeki IP'den farklı olabilir); eski TLS sürümüÇıkış IP'nizi bankaya bildirin; TLS 1.2 ya da üstünü kullanın
Taksit reddediliyorKart başka bir programa ait, banka kartı (debit), yurt dışı kartı ya da sektörel taksit sınırıÖdemeden önce BIN sorgusuyla yalnızca geçerli taksit seçeneklerini gösterin

Oturum sorununun nedeni çoğu zaman gözden kaçar: Bazı tarayıcılar, SameSite niteliği belirtilmemiş çerezleri Lax kabul eder ve Lax çerezler siteler arası POST isteğinde gönderilmez. Varsayılan Lax uygulandığında, son iki dakika içinde yazılmış çerezler için POST'a izin veren bir istisna vardır; bu yüzden hızlı bir testte çalışan akış, SMS ekranında daha uzun kalan gerçek bir müşteride bozulabilir. En sağlam çözüm dönüşü oturuma bağlamamaktır. mews/pos'un örnekleri ise oturum çerezini SameSite=None, Secure ve HttpOnly olarak ayarlar.

Sık sorulan sorular​

Sanal POS nasıl alınır?
Vergi kaydı olan bir işletmeyle ve yasal sayfaları hazır bir siteyle bir bankaya ya da ödeme kuruluşuna başvurursunuz. Onaydan sonra test bilgileriyle entegrasyonu tamamlar, kuruluşun canlıya geçiş kontrolünden sonra canlı ortam bilgilerini alırsınız. Ödeme kuruluşlarında süreç genellikle daha kısadır.

Küçük bir mağaza için banka mı, ödeme kuruluşu mu?
Çoğu zaman ödeme kuruluşu: tek entegrasyonla kart programlarının çoğuna taksit sunar, platform eklentileri hazırdır ve başvuru hızlı sonuçlanır. Hacminiz büyüdüğünde ya da müşterileriniz belirli bir kart programında yoğunlaştığında banka sanal POS'u ekleyip toplam maliyeti yeniden hesaplayın.

Birden fazla banka sanal POS'u kullanabilir miyim?
Evet. İşlemi kartın BIN'ine göre o kartın programına ait bankaya yönlendirirseniz o programın taksitlerini sunabilir, bir banka arıza verdiğinde diğerine geçebilirsiniz. Bedeli, her banka için ayrı entegrasyon, ayrı hesaba geçiş ve ayrı mutabakattır. mews/pos gibi kütüphaneler entegrasyonu kolaylaştırır; yönlendirme kuralları ve mutabakat yine sizin işinizdir.

3D Secure zorunlu mu?
Pratikte evet. Türkiye'deki bankalar ve ödeme kuruluşları e-ticarette varsayılan olarak 3D Secure ister; 3D'siz (NonSecure) işlem yalnızca sözleşmeyle ve sınırlı durumlarda açılır. 3D'siz işlemde sahtekârlık kaynaklı itirazların yükü sizde kalır.

Ortak ödeme sayfası (hosted payment page) nedir?
Kart bilgisinin sizin sitenizde değil, bankanın ya da ödeme kuruluşunun sayfasında girildiği modeldir. Siteniz yalnızca sipariş bilgilerini ve hash'i gönderir; kart verisi sunucularınıza hiç uğramaz. PCI DSS kapsamınız küçülür, buna karşılık sayfanın görünümü üzerindeki kontrolünüz azalır.

mdStatus nedir, hangi değer başarılıdır?
mdStatus, NestPay tabanlı altyapılarda 3D doğrulamasının sonucunu taşıyan alandır ve 1 tam doğrulama anlamına gelir. 2, 3 ve 4 değerleri mews/pos'ta "yarım 3D" (half 3D Secure) olarak sınıflandırılır; bunlar doğrulamanın tam yapılamadığı, yalnızca denendiği durumlardır. Diğer değerler hata ya da başarısızlık bildirir. Kesin anlamları ve hangi değerlerle provizyona devam edebileceğinizi bankanızın dokümanı ve sözleşmeniz belirler.

Sonuç ve sonraki adımlar​

Sanal POS seçimi ticari bir karardır; entegrasyonu ise küçük ama kuralları sıkı bir mühendislik işidir. Doğru modeli seçin, hash'i dokümana birebir uyarak hesaplayın, dönüşü güvenilmeyen girdi sayın ve beklemede kalan ödemeleri durum sorgusuyla tamamlayın.
  1. Kartlı satış hacminizi, ortalama sepet tutarınızı ve taksit ihtiyacınızı çıkarın; en az bir banka ve bir ödeme kuruluşundan yazılı teklif alın.
  2. Sözleşmenizde hangi modelin açık olduğunu öğrenin; mümkünse ortak ödeme sayfasıyla başlayın.
  3. Test ortamında yukarıdaki senaryoları ve sorun giderme tablosundaki durumları tek tek deneyin.
  4. Beklemedeki ödemeler için durum sorgusu görevini ve günlük mutabakatı canlıya çıkmadan önce kurun.
  5. PCI DSS için hangi SAQ'yu dolduracağınızı sağlayıcınızla netleştirin.
Banka ya da ödeme kuruluşu entegrasyonunda destek isterseniz AIOR'un sanal POS entegrasyonu hizmetine göz atabilirsiniz.

A virtual POS (sanal POS in Turkish) is the merchant facility that lets you take card payments online without a physical terminal. A Turkish e-commerce site, subscription service or mobile app that wants to accept cards has two routes: a virtual POS from a bank, or the platform of a licensed payment or electronic money institution. Either way the payment will almost certainly be authenticated with 3D Secure, and the integration will follow one of the models that Turkish bank documents call 3D, 3D Pay or 3D Host.

The first half of this guide is for business owners: how the money moves, value dates and holds, bank POS versus payment institution, and what an application needs. The second half is for developers: the 3D Secure payment flow, the integration models, hash calculation, verifying the callback, testing, voids and refunds, security, and troubleshooting. Field names, hash versions and status codes differ from bank to bank. Our examples are based on real gateways, most of them taken from the source code of the open-source mews/pos library, but check each one against your bank's current integration document before you rely on it.

What is a virtual POS, and how does the money move?​

A card payment has four parties: the cardholder, the issuer (the bank that issued the card), the merchant, and the acquirer that accepts the payment on the merchant's behalf. At a physical terminal the card is inserted or tapped. With a virtual POS the card details are typed into a web form or into the bank's payment page, so the transaction is card-not-present.

In Türkiye two kinds of institution sit on the acquiring side. Banks provide virtual POS facilities under their own merchant agreements. Payment institutions and electronic money institutions operate under Law No. 6493 on Payment and Securities Settlement Systems, Payment Services and Electronic Money Institutions, whose Article 12 lists the acceptance of payment instruments as a payment service. They need an operating license from the Central Bank of the Republic of Türkiye (CBRT, TCMB in Turkish); a 2019 amendment, Law No. 7192, moved that authority from the banking regulator BDDK to the CBRT. iyzico and PayTR, for example, appear on the CBRT's list of electronic money institutions. Before you sign with any provider, confirm that it is on that list or on the list of payment institutions.

The money moves in three steps:
  1. The customer confirms the payment. The bank or payment institution asks the issuer, through the card scheme, for an authorization (provizyon); for domestic transactions, the infrastructure of BKM (the Interbank Card Center) also plays a part in routing and clearing. The approved amount comes off the card's available limit, but it is not yours yet.
  2. At end of day (gün sonu, the batch close) the day's transactions are closed and sent for clearing. The acquirer sets the cut-off time, and the void-versus-refund distinction later in this guide depends on it.
  3. The amount, minus commission, reaches your account on the schedule set in your agreement.

Two Turkish contract terms matter here. Valör is the value date, the day the money becomes usable in your account. Bloke is a hold: the money sits in a blocked account for an agreed number of days, and many agreements lower the commission as the hold gets longer. Installment sales may be paid out to you in one sum, usually at a higher commission, or spread over the installment schedule. The commission rate, the hold period, the payout method for installment sales and any collateral held against chargebacks are all negotiated terms, which is why we quote no rates here. When you ask for an offer, get the rate for each card type and installment count, the hold period and the payout calendar in writing.

Bank virtual POS vs payment institution​

CriterionBank virtual POSPayment or e-money institution
OnboardingThrough a branch or a corporate sales team; risk review, site review, sometimes collateralUsually an online application and faster approval; documents are still checked
PricingNegotiated on volume, card type, installment count and your wider relationship with the bankPublished standard tariff, with room to negotiate at volume
PayoutOften to your account at the same bank; value date and hold set by contractTo the bank account you register; schedule and any hold set by contract
Installments (taksit)Mostly for cards in the bank's own card program (e.g. Bonus at Garanti BBVA, World at Yapı Kredi, Maximum at İş Bankası, Axess at Akbank)Installments on many card programs through one integration
Several banksA separate contract and integration per bank; you design the routing between themHandled by the provider behind the scenes
Integration effortEach bank or POS platform has its own fields, hash rule and test processOne API or SDK; ready-made plugins for common store platforms
Marketplace, sub-merchantsRarely offered to small merchants; negotiated separatelySub-merchant and split-payment products offered by many providers
SupportThe bank's POS helpdesk and integration teamMerchant dashboard, developer docs, sandbox and support channels
PCI DSS scopeDepends on the integration model; smallest with the bank's hosted pageUsually smallest with the hosted form or iframe

For a new, small store a payment institution is usually the quickest route: one integration, installments on most card programs, and plugins ready to install. As revenue grows, total cost starts to dominate. If a large share of your customers use one card program, a common hybrid is a virtual POS from that program's bank plus the payment institution for every other card. Do not decide on the headline commission alone; weigh the hold period, refund and chargeback handling, and the work of reconciling two sources. If you offer installments at the cash price, the installment cost is yours; if you pass it on to the customer as an installment surcharge (vade farkı), show the total clearly at checkout. Our thread on choosing a payment provider compares providers' features and fees.

How to get a virtual POS in Turkey: application and documents​

Requirements vary by institution; these are the most common:
  • A registered business (sole proprietorship, limited or joint-stock company) with its tax certificate (vergi levhası) and signature circular (imza sirküleri; sole proprietors give a signature declaration, imza beyannamesi); for companies also the trade registry gazette and an activity certificate; ID details of the authorized people.
  • A business bank account (IBAN) for payouts. Banks usually want it opened with them.
  • A live HTTPS site whose pages clearly show products, prices and contact details (legal name, address, phone, email).
  • Legal pages: distance sales contract, pre-information form, withdrawal, cancellation and return terms, privacy policy, and the notice required by KVKK, the Turkish data protection law.
  • Registration with ETBİS, Türkiye's e-commerce information system, if you sell from your own site.
  • Your line of business, average basket and expected monthly volume. Some sectors need extra review or collateral, and some are not accepted at all.
Once approved, you receive a merchant ID, an API user and password, a store key for 3D transactions and test environment details. Live credentials can move money, so keep them in environment variables or a secret manager, never in the code repository.

What is 3D Secure? The payment flow step by step​

3D Secure is the protocol in which the issuer authenticates the cardholder at the moment of payment. The "3D" stands for three domains: the acquirer domain (merchant and acquirer), the interoperability domain run by the card schemes, and the issuer domain. Today's version is EMV 3-D Secure, 3DS 2.x, published by EMVCo. The current specification bulletins on EMVCo's page cover versions 2.2.0 to 2.3.1.1, and EMVCo recommends 2.2 or higher; a 2.4.0 draft went out for comment in June 2026. The major card brands no longer support the legacy 3DS 1 protocol.

The components:
  • 3DS Requestor: the party that starts authentication, i.e. the merchant.
  • 3DS Server: builds the authentication request. With a Turkish bank's virtual POS, the bank's 3D gateway normally plays this role; you send the browser to it with a form.
  • Directory Server (DS): run by a card scheme such as Visa or Mastercard; it routes the request to the card's issuer.
  • ACS (Access Control Server): the issuer's server that authenticates the cardholder. The SMS one-time password screen or the banking app's approval prompt comes from here.

The flow in four steps:
  1. The customer enters the card details. The 3DS Server sends card and transaction data (amount, currency, browser and device data) through the DS to the ACS.
  2. The ACS runs a risk assessment. If it rates the risk as low, it authenticates without asking the customer anything: the frictionless flow. Otherwise it asks for a password or an app approval: the challenge flow. Turkish shoppers know the challenge as the SMS code screen.
  3. The result comes back with a cryptographic authentication value (CAVV or AAV) and an ECI (Electronic Commerce Indicator).
  4. Only then is the payment authorized: the authorization request carries the authentication data to the issuer, which approves or declines based on the limit, the card's status and its own rules.

The most common misconception is that a successful 3D authentication means a successful payment. Authentication proves who the cardholder is; authorization decides whether the money can be taken. A transaction that passes 3D and is then declined for insufficient funds is perfectly normal. 3DS 2 also defines an SDK for mobile apps, so authentication can happen inside the app instead of in a browser.

ECI values are scheme-specific. For example, on Visa 05 means fully authenticated and 06 an attempt; on Mastercard 02 means fully authenticated and 01 an attempt. Store the ECI and CAVV from the callback with your transaction record; they are evidence when you contest a chargeback.

Liability shift: when a cardholder raises a fraud dispute ("I did not make this purchase") on a successfully authenticated transaction, the fraud loss generally moves from the merchant to the issuer. It is not a guarantee: the card scheme's rules, the ECI and your agreement decide. It does not cover non-fraud disputes such as "item not received" or "not as described" at all; there, your shipping and delivery evidence decides the outcome.

Integration models: NonSecure vs 3D vs 3D Pay vs 3D Host​

NonSecure (2D, without 3D): your server sends the card details to the bank's API in a single request, with no 3D authentication. Banks usually enable this model only by special permission, and the merchant carries the cost of fraud disputes.

3D (3D Secure, "3D Model"): the card form is on your page, and the browser posts the card details to the bank's 3D gateway. The bank only authenticates and sends the result to your callback URL. To take the money, your server then sends a separate authorization (provision) request. The two-step design lets you see the authentication result and run stock or fraud checks before the authorization.

3D Pay: the form is still on your page, but after authentication the bank also runs the authorization itself. The callback carries the payment result, and there is no separate provision call.

3D Host and 3D Pay Hosting (hosted payment page): the customer types the card details on a page hosted by the bank, so your page never sees card data. In NestPay's 3D Pay Hosting the bank also authorizes the payment after that page, and mews/pos settles NestPay's 3D Host callback without a separate provision call as well. Hosted-page variants differ between platforms, so confirm in your document whether the callback reports a completed payment or only the authentication result. Payment institutions' hosted forms, such as iyzico's Checkout Form or PayTR's iFrame API, are the equivalent of this model.

ModelWho collects the card data?Typical PCI DSS SAQServer-to-bank callsUser experience
NonSecureYour serverSAQ D1 payment requestNo redirect; highest risk
3DYour pageSAQ A-EP if the form posts straight to the bank, SAQ D if card data passes through your server1 provision request after authenticationYour own form, then the bank's authentication screen
3D PayYour pageSame as 3DNone; a status query when neededSame as 3D
3D Host, 3D Pay HostingThe bank's pageUsually SAQ ANone on NestPay; check the document for other platformsHand-off to the bank's page; limited design options

These names are not a standard; they change with the POS platform. According to the mews/pos documentation, the Asseco (Payten) NestPay platform used by banks including İş Bankası, Halkbank, Ziraat Bankası and TEB takes the model in a storetype field with values 3d, 3d_pay, 3d_pay_hosting or 3d_host. Garanti BBVA carries the same information in a secure3dsecuritylevel field with values such as 3D or 3D_PAY. Some platforms, such as Yapı Kredi's PosNet, the PayFlex platform used by VakıfBank, and Kuveyt Türk's virtual POS, require your server to call the bank's API before it can build the 3D form. The open-source PHP library mews/pos wraps these differences in one interface; version 2.0.0 shipped in July 2026 and requires PHP 8.0 or later. Even with a library, read your bank's document: it lists the supported models and explains the response codes. Confirm with the bank which model your agreement enables.

Virtual POS integration step by step​

The flow below is the common skeleton of the three 3D models; each step notes where they differ.

Code:
Browser --(form POST: order, amount, hash)--> Bank's 3D gateway
3D gateway --> Directory Server --> ACS (SMS code or app approval)
ACS --> 3D gateway: authentication result (mdStatus, ECI, CAVV)
(3D Pay and 3D Pay Hosting) the 3D gateway runs the authorization itself
3D gateway --(POST through the browser: result and hash)--> okUrl / failUrl
(3D) Your server --(provision request: md, xid, eci, cavv)--> Bank's API

1. Preparation​

Get these from the bank: test and live 3D gateway URLs, the API URL, the merchant ID, the API user and password, the store key, test cards, and the model your agreement enables. Some banks accept API calls only from registered IP addresses. On shared hosting the server's outgoing IP can differ from the address shown in your control panel, so confirm the outgoing IP and register that one.

2. Build the request​

  • Order ID: generate a unique ID for each payment attempt and link it to your order. Rules on reusing an order ID differ between platforms; a fresh ID for every retry after a failed attempt removes the ambiguity.
  • Amount: store amounts as integer kuruş (minor units) and convert to the document's format only when sending. Per the mews/pos source code, NestPay-based platforms take a decimal amount with a dot (the library sends 149.90 TRY as 149.9), while Garanti BBVA and PosNet expect integer kuruş (14990). Never use locale formatting (1.234,50). The amount string in the hash must match the string in the form exactly.
  • Currency: most platforms use the ISO 4217 numeric code: 949 for the Turkish lira, 840 for the US dollar, 978 for the euro. According to mews/pos, PosNet instead expects letter codes such as TL, US and EU.
  • Installments: in mews/pos a single payment goes to NestPay as an empty value and to PosNet as 00; an installment sale carries the installment count. Installments apply only to credit cards; debit cards and cards issued abroad are usually processed as single payments. Installment counts are capped by regulation in some sectors, and the caps change from time to time, so ask your bank for the current limit for your product group.
  • Callback URLs: HTTPS addresses for success and failure (such as okUrl and failUrl). They must not redirect. Some platforms accept only one address, so using the same URL for both keeps things simple.
  • Other fields: a random value, language, transaction type and model. Field names differ too: NestPay uses clientid, oid, amount, currency, taksit, okUrl, failUrl, rnd, storetype and hashAlgorithm, while Garanti BBVA uses orderid, txnamount, txncurrencycode, txninstallmentcount, successurl and errorurl.

3. Compute the hash​

The hash proves that the request came from you and was not altered on the way: a request whose amount was changed in the browser no longer matches its hash, and the bank rejects it. It is computed with the store key that only you and the bank know, and the key never goes to the browser. The rules differ sharply between platforms:
  • NestPay, hashAlgorithm=ver3 (mews/pos implementation): all fields except hash and encoding are sorted by name, case-insensitively. Backslashes and pipe characters in the values are escaped with a backslash, the values are joined with a pipe (|), and the store key is appended. The SHA-512 digest of that string is Base64-encoded.
  • Garanti BBVA (mews/pos implementation): specific fields are concatenated in a fixed order with no separator, followed by the store key and a security value derived with SHA-1 from the provision password and the terminal ID; the result is an uppercase hexadecimal SHA-512 digest. The callback lists the fields that went into its hash in a hashparams field.
One platform may support several hash versions. Whatever version you declare in the request, verify the callback with that version's rule.

4. Send the browser to the bank​

Your server renders an HTML form that carries the fields and the hash as hidden inputs; the form POSTs to the bank's 3D gateway. In 3D Host the form has no card fields and can submit itself as soon as the page loads (auto-submit). In 3D and 3D Pay there are two options: the card fields sit in a form the customer fills in and that posts straight to the bank, or your server receives the card first and renders the form itself. The second option expands your PCI DSS scope to SAQ D, so choose it knowingly.

5. Verify the callback and update the order​

The bank POSTs the result to your callback URL through the customer's browser. Because the request passes through the browser, its fields may have been tampered with; treat every field as untrusted input:
  1. Recompute the hash with the document's callback rule and compare it in constant time, for example with PHP's hash_equals.
  2. Look up the attempt by its order ID. If its status is no longer pending, the payment has already been settled; do not process it twice.
  3. Check that the returned amount and currency match your record.
  4. Check the authentication status. NestPay calls this field mdStatus and Garanti BBVA calls it mdstatus. mews/pos accepts only 1 as a success for NestPay but accepts 1 to 4 for Garanti BBVA; which values you accept is set by your bank's document and your agreement.
  5. In the 3D model, run the authorization now: with NestPay, the callback's md, xid, eci and cavv values go into the provision request as Number, PayerTxnId, PayerSecurityLevel and PayerAuthenticationCode respectively. In 3D Pay and 3D Pay Hosting the payment result is already in the callback; NestPay approval requires ProcReturnCode=00 together with mdStatus=1.
  6. Update the attempt and the order in the same database transaction, and only if the status is still pending. Then redirect to a result page with 303 See Other (Post/Redirect/Get), so a page refresh does not resend the POST.

The callback may never arrive: the customer can close the tab on the authentication screen or lose the connection. Run a scheduled job that settles attempts pending for longer than a set time through the bank's status query (ORDERSTATUS on NestPay and orderinq on Garanti BBVA, per mews/pos), and reconcile your records with the bank's transaction list every day. Some providers also send a server-to-server notification; for signature checks, retries and audit trails in such a receiver, see our thread on webhook receivers.

The PHP skeleton below shows these steps. Field names, URLs and status codes are deliberately generic; this is not any bank's real specification.

PHP:
<?php
// SKELETON ONLY: field names, URLs, status codes and the hash rule are placeholders,
// not any bank's specification. Take the real ones from your bank's document.
declare(strict_types=1);

const GATEWAY_URL = 'https://3d-gateway.bank.example/pay';   // from the document
const RETURN_URL  = 'https://shop.example.com/payment/return';

function storeKey(): string
{
    $key = getenv('POS_STORE_KEY');   // from a secret store; never sent to the browser
    if ($key === false || $key === '') {
        throw new RuntimeException('POS_STORE_KEY is not set');
    }
    return $key;
}

// One documented style (NestPay ver3 as implemented in mews/pos): sort the fields other than
// hash and encoding by name, escape \ and |, join with |, append the store key, SHA-512, Base64.
function bankHash(array $fields): string
{
    foreach (array_keys($fields) as $name) {
        if (in_array(strtolower((string) $name), ['hash', 'encoding'], true)) {
            unset($fields[$name]);   // callbacks may send it in upper case, e.g. HASH
        }
    }
    ksort($fields, SORT_NATURAL | SORT_FLAG_CASE);
    $values   = array_values($fields);
    $values[] = storeKey();
    $escaped  = array_map(
        fn ($v): string => str_replace(['\\', '|'], ['\\\\', '\\|'], (string) $v),
        $values
    );
    return base64_encode(hash('sha512', implode('|', $escaped), true));
}

function startPayment(PDO $db, int $orderId, int $amountKurus, int $installments): string
{
    $attemptId = $orderId . '-' . bin2hex(random_bytes(4));   // a new ID for every attempt
    $db->prepare("INSERT INTO payment_attempts (id, order_id, amount_kurus, status)
                  VALUES (?, ?, ?, 'pending')")
       ->execute([$attemptId, $orderId, $amountKurus]);

    $fields = [
        'merchant_id' => (string) getenv('POS_MERCHANT_ID'),
        'order_id'    => $attemptId,
        'amount'      => number_format($amountKurus / 100, 2, '.', ''),  // some gateways want minor units
        'currency'    => '949',                                          // ISO 4217 numeric code for TRY
        'installment' => $installments > 1 ? (string) $installments : '',
        'ok_url'      => RETURN_URL,
        'fail_url'    => RETURN_URL,
        'model'       => '3d_pay_hosting',                               // model name per the document
        'random'      => bin2hex(random_bytes(10)),
    ];
    $fields['hash'] = bankHash($fields);

    $inputs = '';
    foreach ($fields as $name => $value) {
        $inputs .= sprintf('<input type="hidden" name="%s" value="%s">',
            htmlspecialchars($name), htmlspecialchars($value));
    }
    return '<form id="pay" method="post" action="' . htmlspecialchars(GATEWAY_URL) . '">'
        . $inputs . '</form><script>document.getElementById("pay").submit();</script>';
}

function handleReturn(PDO $db, array $post): void
{
    $attemptId = (string) ($post['order_id'] ?? '');
    if (!hash_equals(bankHash($post), (string) ($post['hash'] ?? ''))) {
        error_log("payment return: hash mismatch, attempt {$attemptId}");   // never log card data
        http_response_code(400);
        return;
    }

    $db->beginTransaction();
    $stmt = $db->prepare('SELECT amount_kurus, status FROM payment_attempts WHERE id = ? FOR UPDATE');
    $stmt->execute([$attemptId]);
    $attempt = $stmt->fetch(PDO::FETCH_ASSOC);

    if ($attempt !== false && $attempt['status'] === 'pending') {
        $amountOk   = (int) round((float) ($post['amount'] ?? 0) * 100) === (int) $attempt['amount_kurus'];
        $currencyOk = ($post['currency'] ?? '') === '949';
        $authOk     = ($post['auth_status'] ?? '') === '1';      // e.g. mdStatus; meanings differ per bank
        $paidOk     = ($post['response_code'] ?? '') === '00';   // 3D Pay or hosted page: approval code
        // 3D model instead: call the provision API here with the authentication data
        // from this callback and take $paidOk from its response.

        $status = ($amountOk && $currencyOk && $authOk && $paidOk) ? 'paid' : 'failed';
        $db->prepare("UPDATE payment_attempts SET status = ?, bank_ref = ?
                      WHERE id = ? AND status = 'pending'")
           ->execute([$status, $post['bank_ref'] ?? null, $attemptId]);
        // When paid, mark the order paid inside this same transaction.
    }
    $db->commit();

    header('Location: /payment/result?attempt=' . rawurlencode($attemptId), true, 303);
}

Testing, voids and refunds​

Take the test environment URL, test merchant credentials, test cards and the authentication password from your bank's document, and never use real cards on the test system. Before going live, run at least these scenarios:
  • A successful single payment and an installment payment; an amount with kuruş (e.g. 1.01 TRY).
  • A wrong password, canceling on the authentication screen, and closing the tab on the authentication screen.
  • A decline for limit or insufficient funds (if the document provides such a test card).
  • Refreshing the result page, the back button, and a double click on the pay button.
  • A same-day void; a full and a partial refund after end of day.
  • Rejection of a callback with a tampered hash.

OperationWhen to use itWhat the cardholder seesTransaction type (NestPay / Garanti BBVA)
SaleAt checkout; authorization and capture togetherA chargeAuth / sales
Pre-authorization (ön provizyon)To reserve an amount and finalize it later: hotels, car rental, stock confirmationA hold on the limitPreAuth / preauth
Post-authorization (provizyon kapama)To turn a pre-authorization into a final saleA chargePostAuth / postauth
Void (iptal)Before end of day; usually the full amount onlyThe hold is released; usually no statement lineVoid / void
Refund (iade)After end of day; full or partial amountA separate credit line; timing depends on the issuerCredit / refund
The transaction type names come from the mews/pos source code. Confirm in your document whether a partial amount can be refunded before end of day and whether refunds need a separate user or password; mews/pos, for instance, signs Garanti BBVA voids and refunds with a separate refund password. Your order system should not tell the customer "your refund has been issued" until the bank's refund response has come back successful.

Security and PCI DSS compliance​

PCI DSS is the security standard the card schemes expect from anyone who processes, stores or transmits card data. The PCI SSC document library lists PCI DSS v4.0.1 as the current version. Your acquiring bank or payment institution decides which self-assessment questionnaire (SAQ) you complete and how often you attest. The practical rules:
  • Do not store the card number (PAN) or the security code (CVV2/CVC2). PCI DSS does not allow the security code to be kept after authorization, even encrypted. If you need card-on-file payments, use your provider's tokenization service.
  • Use a hosted payment page or hosted form where you can; no other decision shrinks scope as much. SAQ A still leaves obligations: according to PCI SSC FAQ 1604 (June 2026), ASV external vulnerability scans are part of SAQ A for pages that redirect customers or embed an iframe. If you embed the payment form in an iframe, FAQ 1588 (February 2025) also requires you to confirm that your page is protected against script attacks.
  • If the card form is on your own page, note PCI DSS v4 requirements 6.4.3 (inventory and authorization of payment page scripts) and 11.6.1 (change and tamper detection), and remove analytics, live chat and advertising scripts from the payment page.
  • Never write raw form bodies to logs. Filter the card number, security code and expiry date fields; the attempt ID, the masked card number returned by the bank and the result codes are enough.
  • Use TLS 1.2 or later, keep the store key in a secret manager, and ask the bank for a new one if you suspect a leak. Limit access to the merchant dashboard to named accounts with two-factor authentication.

Basic fraud rules:
  • Velocity rules: limit the number of attempts from the same IP address, card, device or account within a short window.
  • Card testing: runs of small, failed attempts are the typical sign of this attack. Apply rate limits, a CAPTCHA and temporary blocks.
  • Score mismatches between the country of the card's BIN (its first 6 or 8 digits) and the countries of the IP address and the shipping address.
  • Set amount thresholds, and send cases such as a high first order or many different cards on one account to a manual review queue.
3D Secure is a strong barrier against payments made with stolen card details, but it does not solve account takeover or cardholders disputing their own purchases (friendly fraud).

Virtual POS hash errors and other common problems​

SymptomLikely causeFix
"Hash error" on the requestWrong field order or a missing field; hash version mismatch; the amount string in the hash differs from the form (149.9 vs 149.90); test and live store keys mixed up; stray whitespace or character encodingOn the test system, log the hash input with the store key and card fields masked (in mews/pos's NestPay implementation, the card number and CVV enter the hash when the card passes through your server); compare field order and format with the worked example in the document, and add fields one at a time
Callback hash does not verifyThe bank added a field that is not part of the hash (mews/pos excludes nationalidno, which Ziraat callbacks may contain, after Asseco confirmed it is not hashed); hashing every field when the bank lists them in hashparams, or the reverse; encoding of names and addresses with Turkish charactersApply the document's callback rule exactly; check the encoding field
mdStatus is not 1Card not enrolled in 3D, wrong password, ACS error, or the customer gave upRead mdErrorMsg and the bank's code table; whether values 2 to 4 may proceed is a contract matter
The callback never arrivesA 301/302 redirect on okUrl (the browser turns the POST into a GET and drops the body); a WAF or bot filter blocks the POST; certificate errors; the customer closed the tabUse the final URL with no redirects, add a WAF exception, and settle the rest with a status query
Session lost on return, cart looks emptyThe session cookie is SameSite=Lax (explicitly or by browser default) and is not sent with the cross-site POSTMake the callback stateless (find the attempt by its ID) or set the session cookie to SameSite=None; Secure
419 Page Expired (Laravel) or another CSRF errorThe framework's CSRF protection rejects the bank's POSTExclude the callback route from CSRF checks and keep hash verification mandatory
Amount or currency errorsLocale formatting (1.234,50), floating-point rounding, sending TRY instead of 949, lira and kuruş mixed upKeep amounts as integer kuruş and format them as the document specifies
Duplicate orders or fears of a double chargePage refresh, double click, the callback and a notification arriving togetherConditional update (WHERE status = 'pending'), Post/Redirect/Get with 303, a separate ID per attempt
The provision request times outNetwork failure; the bank may have answered after your client gave upDo not retry blindly; query the status first
API calls rejected or connection refusedYour server's outgoing IP is not registered at the bank (on shared hosting it may differ from the panel's IP); an old TLS versionRegister the outgoing IP with the bank; use TLS 1.2 or later
Installment option rejectedThe card belongs to another program, is a debit card or a foreign card, or a sector installment cap appliesRun a BIN lookup before payment and show only valid installment options

The session problem is easy to miss. Some browsers treat a cookie without a SameSite attribute as Lax, and Lax cookies are not sent with cross-site POST requests. When Lax is applied by default there is an exception that allows POST for cookies set within the previous two minutes, which is why a flow that works in a quick test can break for a real customer who spends longer on the SMS screen. The most reliable fix is a callback that does not depend on the session at all. The mews/pos examples, for their part, set the session cookie to SameSite=None, Secure and HttpOnly.

FAQ​

How do I get a virtual POS in Turkey?
Apply to a bank or a payment institution with a registered business and a site whose legal pages are in place. After approval you finish the integration with test credentials, and you receive live credentials once the provider's go-live check passes. Payment institutions usually take less time.

Bank or payment institution for a small store?
Usually a payment institution: one integration gives installments on most card programs, platform plugins are ready, and approval is quick. When your volume grows or your customers cluster in one card program, add a bank virtual POS and recalculate the total cost.

Can I use virtual POS facilities from several banks?
Yes. If you route each payment by the card's BIN to the bank that runs the card's program, you can offer that program's installments and fail over when one bank has an outage. The price is a separate integration, a separate payout and a separate reconciliation for every bank. Libraries such as mews/pos make the integration easier; the routing rules and the reconciliation remain your job.

Is 3D Secure mandatory?
In practice, yes. Banks and payment institutions in Türkiye require 3D Secure for e-commerce by default; NonSecure (2D) processing is enabled only by agreement and in limited cases. Without 3D, you carry the cost of fraud disputes.

What is a hosted payment page?
It is the model in which the card details are entered on the bank's or payment institution's page rather than on your site. Your site sends only the order data and the hash, and card data never touches your servers. Your PCI DSS scope shrinks, and in exchange you have less control over how the page looks.

What is mdStatus, and which value means success?
mdStatus is the field that carries the 3D authentication result on NestPay-based platforms, and 1 means full authentication. mews/pos classifies 2, 3 and 4 as "half 3D Secure": cases where authentication was only attempted rather than completed. Other values report errors or failures. The exact meanings, and which values let you proceed to authorization, come from your bank's document and your agreement.

Conclusion and next steps​

Choosing a virtual POS is a commercial decision; integrating one is a small engineering job with strict rules. Pick the right model, compute the hash exactly as documented, treat the callback as untrusted input, and settle pending payments with status queries.
  1. Work out your card sales volume, average basket and installment needs, and get written offers from at least one bank and one payment institution.
  2. Find out which models your agreement enables; start with the hosted payment page if you can.
  3. On the test system, work through the scenarios above and every case in the troubleshooting table.
  4. Set up the status-query job for pending payments and the daily reconciliation before you go live.
  5. Agree with your provider which SAQ you must complete for PCI DSS.
If you would like help with a bank or payment institution integration, see AIOR's virtual POS integration service.
 
Last edited:

Forum statistics

Threads
925
Messages
932
Members
26
Latest member
Jessicawatson

Members online

No members online now.

Featured content

AIOR
AIOR TECHNOLOGY

Get a quote for all your needs

Hosting · Domain · Servers · Design · Software · Engineering · Industry Solutions

Get a quote

24/7 Support · Quick response

Back
Top