nestpay — NestPay (Asseco SEE)
> Tip: Banka POS bayisi (12 banka aynı altyapı) > Adapter: InvumPosGatewayNestPayNestPayGateway (planlanan, Hafta 17 sprint) > Son senkron: 2026-05-05 (clean-room spec — CP.VPOS v2 referans) > Last-known-good API: Asseco-SEE EST/CC5 v1 (XML over HTTPS) + EST 3D Gate (form-POST hash ver3)
NestPay (eski adıyla EST) Asseco SEE'nin sanal POS altyapısı — Türkiye'de 12 banka aynı API'yı kullanıyor (her bankanın kendi sub-domain'i, ama endpoint path'leri / request body / response shape'i identical). Tek adapter ile 12 PG kazanılır; per-bank fark sadece base URL ve credential.
> NestPay olmayan Akbank: Akbank Mart 2024'te kendi modern REST > API'sına geçti (api.akbank.com/api/v1/payment/virtualpos/...). > Eski NestPay endpoint'i (sanalakpos.com/fim/api) hâlâ çalışıyor > ama merchant'lar yeni başvurularda doğrudan REST API'a yönlendiriliyor. > Faz 2 backlog: ayrı AkbankGateway adapter'ı (NestPay'den ayrı).
1. Bağlantı bilgileri
1.1. Per-bank base URL'ler
NestPay altyapısını kullanan bankalar (CP.VPOS v2 referansından, 2026-05-05 itibariyle):
| Banka | Production base | Sandbox base | |————————-|————————————————–|———————————————————–| | Akbank (legacy) | https://www.sanalakpos.com | https://entegrasyon.asseco-see.com.tr (genel sandbox) | | AlternatifBank | https://sanalpos.abank.com.tr | https://entegrasyon.asseco-see.com.tr | | Anadolubank | https://anadolusanalpos.est.com.tr | https://entegrasyon.asseco-see.com.tr | | Cardplus | https://sanalpos.card-plus.net | https://entegrasyon.asseco-see.com.tr | | Halkbank | https://sanalpos.halkbank.com.tr | https://entegrasyon.asseco-see.com.tr | | ING Bank | https://sanalpos.ingbank.com.tr | https://entegrasyon.asseco-see.com.tr | | İşbank | https://sanalpos.isbank.com.tr | https://istest.asseco-see.com.tr ⚠️ banka-spesifik | | QNB Finansbank | https://www.fbwebpos.com | https://entegrasyon.asseco-see.com.tr | | Şekerbank | https://sanalpos.sekerbank.com.tr | https://entegrasyon.asseco-see.com.tr | | TEB | https://sanalpos.teb.com.tr | https://entegrasyon.asseco-see.com.tr | | Türkiye Finans | https://sanalpos.turkiyefinans.com.tr | https://entegrasyon.asseco-see.com.tr | | Ziraat Bankası | https://sanalpos2.ziraatbank.com.tr | https://torus-stage-ziraat.asseco-see.com.tr ⚠️ banka-spesifik |
Path'ler (her banka için aynı):
/fim/api— XML API (sale, refund, cancel, sale-finalize, status query)/fim/est3Dgate— 3D Secure form-POST gateway
Sandbox notu: 12 bankanın 10'u Asseco SEE'nin paylaşımlı sandbox'ı (entegrasyon.asseco-see.com.tr) — herhangi bir test merchant credential'ı ile çalışılır. İşbank ve Ziraat kendi sandbox host'unu kullanıyor (banka-spesifik test merchant gerek).
1.2. Yetkili / dev portal
Her banka kendi POS başvuru sürecini yürütür; ortak Asseco SEE dev portal yok. Banka temsilcisinden store key (4. credential — 3D hash secret) ve client ID + Name + Password alınır.
Bizim görevimiz: her merchant kendi bankasından credential aldıktan sonra, WCGateway settings'e enter eder. Adapter slug nestpay_<bank> şeması ile çoklu instance kaydedilir (WC'nin per-method seçeneği gibi).
2. Auth şeması
> Kategori: XML body POST + 3DS form-POST hash (hashAlgorithm=ver3)
NestPay iki ayrı request shape kullanıyor:
2.1. XML API endpoint (/fim/api)
Auth basit: her XML body içine Name / Password / ClientId üç field gömülür. Ek imza/hash YOK — TLS + credential bilgilerinin gizliliği kâfi sayılıyor.
Form-encoded body wrapper:
DATA=<xml-body>
Content-Type: application/x-www-form-urlencoded. Yani XML form-data field değeri olarak yollanır (HTTP body bütün olarak XML değil).
2.2. 3DS gateway endpoint (/fim/est3Dgate)
Bu endpoint browser'dan form POST alır (auto-submit form, kart bilgileri müşterinin browser'ından bankaya). Her parametre + storeKey üzerinden SHA512 hash üretilir, hash field olarak eklenir.
Hash formülü (ver3):
- Tüm request param'larını alfabetik sort et (key'e göre)
- Her value içinde
|vekarakterlerini escape et:
– | → | – → \
- Sort edilmiş value'ları
|ile join et - Sonuna
|<storeKey>ekle - UTF-8 olarak SHA512 al, Base64 encode
hash_input = "value1|value2|value3|...|valueN|storeKey"
hash = base64( sha512_utf8( hash_input ) )
Sırlar (encrypted): client_id (ClientId), merchant_user (Name), merchant_password (Password), store_key (3D hash secret).
3. Endpoint tablosu
Her banka aynı path'leri kullanır; sadece base URL değişir.
| İşlem | Method | Path | İstek field'ları (kritik) | Yanıt field'ları (kritik) | |———————-|——–|———————|———————————————————————————————————-|————————————————————————| | 3D init | POST | /fim/est3Dgate | clientid, oid, amount, currency, taksit, installment, okUrl, failUrl, rnd, storetype=3d, lang=tr, islemtipi=Auth, hashAlgorithm=ver3, pan, cv2, Ecom_Payment_Card_ExpDate_{Year,Month}, hash | HTML auto-submit form (browser bank ACS'e yönlendirilir) | | 3D callback | (POST'a bizim endpoint) | (bizim /wp-json/invum-pos/v1/nestpay/callback) | mdStatus (1=success), oid, md, xid, eci, cavv, clientIp, installment | (no response — biz redirect ederiz) | | Sale finalize | POST | /fim/api | Name, Password, ClientId, IPAddress, OrderId, Taksit, Type=Auth, Number=md, PayerTxnId=xid, PayerSecurityLevel=eci, PayerAuthenticationCode=cavv | <Response>Approved/Error/Decline</Response>, <TransId>...</TransId>, <ErrMsg>...</ErrMsg> | | Sale (non-3D) | POST | /fim/api | Name, Password, ClientId, Type=Auth, OrderId, Taksit, Total, Currency, Number=PAN, Expires=MM/YYYY, Cvv2Val | <Response>...</Response>, <TransId>...</TransId> | | Refund | POST | /fim/api | Name, Password, ClientId, Type=Credit, TransId, Total | <Response>Approved/Error</Response>, <ErrMsg>...</ErrMsg> | | Cancel (void) | POST | /fim/api | Name, Password, ClientId, Type=Void, TransId | <Response>...</Response> | | Status query | POST | /fim/api | Name, Password, ClientId, OrderId, Extra: { ORDERSTATUS: QUERY }. Wrapper root tag <CC5Request>. | <CC5Response> → <Response>, <TransId>, <Extra> (CAPTURE_AMT, CAPTURE_DTTM, TRANS_STAT: S=Paid, V=Voided) |
Test kartları: Asseco SEE genel sandbox için banka-spesifik test kartı listesi — her banka kendi başvuru paketinde verir. NestPay'in ortak bir test PAN'ı YOK (banka temsilcisinden istenecek).
4. Bilinen quirk'ler
> Üretimde / sandbox'ta yaşanmış spesifik problemler. Banka POS sprint'i > başlamadan, CP.VPOS implementation'ından spec çıkarımları:
#1 — Amount format Türkçe-locale, sonra normalleştirme
Amount string "N2" Türkçe locale formatlanıyor (1.234,56) → sonra nokta silip virgül noktaya çevirme (1234.56) yapılıyor:
1234.56 (decimal) → "1.234,56" (tr-TR N2) → "1234,56" (replace ".") → "1234.56" (replace ",")
Sonuç 2-haneli decimal, dot separator, thousand separator yok.
number_format( $amount, 2, '.', '' ) PHP'de aynı sonucu üretir, ama locale formatı süreciyle eşleştiğine emin ol — bazı eski merchant'lar "1234,56" beklemeye programlanmış olabilir.
#2 — Hash escape sırası kritik
| ve escape sırası önemli:
- ÖNCE
|→| - SONRA
→\
Yanlış sırayla yapılırsa (önce , sonra |) escape karakterlerinin kendileri tekrar escape edilir → hash mismatch. Adapter'da test için fixture: "a|bc" → "a|b\c".
#3 — installment ve taksit aynı değer (legacy duplicate)
3DS init form'unda hem installment hem taksit field'ları var, ikisi de aynı değer alır. Eski API versiyonlarından kalma duplicate; ikisi gönderilmezse bazı bankalar reject ediyor.
installment=3 → form: installment=3 + taksit=3
installment=1 (peşin) → form: installment="" + taksit="" (boş string!)
Tek çekim için boş string zorunlu — 0 veya 1 gönderirsen banka "taksit yapısı geçersiz" döndürebilir.
#4 — Currency ISO-4217 numeric (string olarak)
Currency field numeric ISO 4217:
- TRY =
949 - USD =
840 - EUR =
978
CP.VPOS ((int)Currency).ToString() ile gönderiyor — int → string. Bizim adapter (string) 949 ile aynı sonucu verir.
#5 — <CC5Request> / <CC5Response> wrapper sadece query'de
Sale, refund, cancel, sale-finalize tek root tag <CC5Request> veya <CC5Response> ile sarılmıyor — direct param'lar XML root'unda. Sadece SaleQuery için CC5 wrapper kullanılıyor (Extra: { ORDERSTATUS: QUERY } field'ı sebebiyle). Bu küçük tutarsızlık spec'te dökümante değil ama gerçek API davranışı.
#6 — Response field değerleri: Approved / Error / Decline / Declined
Hem Decline hem Declined görüyor (Asseco'nun farklı sürümlerinde farklı string). Adapter ikisini de tanımalı — sadece Error veya Decline* başlangıçlı stringleri fail saymalı, geri kalanı Approved varsay. CP.VPOS pattern'i:
if ( in_array( $response, ['Error', 'Decline', 'Declined'], true ) ) { /* fail */ }
elseif ( $response === 'Approved' ) { /* success */ }
#7 — TLS 1.2 zorunlu (eski .NET workaround)
CP.VPOS ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072 ile TLS 1.2'yi forcelluyor. PHP 8.1+ default zaten TLS 1.2+ kullanır, ama wp_remote_post sslverify=true flag'i ile sertifika doğrulaması açık tutulmalı.
#8 — hashAlgorithm=ver3 zorunlu, eski hash'leri kullanma
NestPay eskiden 3 farklı hash algoritması destekliyordu (ver1, ver2, ver3). Yeni başvurularda sadece ver3 kullanılır; ver1/ver2 deprecated. Hash formülü §2.2'deki gibi.
#9 — Akbank legacy vs modern ayrımı
Akbank Mart 2024'te kendi REST API'sını yayınladı (api.akbank.com/api/v1/payment/virtualpos/...). Yeni başvuru yapan merchant'lar buraya yönlendiriliyor. Ama eski NestPay endpoint'i (sanalakpos.com/fim/api) hâlâ çalışıyor — eski sözleşmesi olan merchant'lar için.
Bizim nestpay_akbank slug'ı eski endpoint'i temsil eder. Yeni Akbank REST için ayrı bir akbank slug'ı (Faz 2 backlog) yazılacak.
#10 — IPAddress field 3D-finalize'da zorunlu
Sale-finalize XML'inde IPAddress field callback'ten gelen clientIp ile dolu olmak zorunda. Boş string geçilirse banka "fraud risk" reddi döndürüyor. CP.VPOS callback'teki responseArray["clientIp"]'i alıp finalize'a yansıtıyor — bizim de aynısını yapmalıyız.
5. Bizim implementasyon notları
- WCGateway sınıfı:
InvumPosGatewayNestPayNestPayWCGateway(per-bank instance:nestpay_akbank,nestpay_halkbank, …) - Charge adapter:
InvumPosGatewayNestPayNestPayGateway(charge + callback verify + status, per-bank URL constructor injection) - Refund adapter:
InvumPosGatewayNestPayNestPayRefund(RefundInterface, R3 router IPTAL/IADE same-day vs post-settle) - Status query:
InvumPosGatewayNestPayNestPayStatusQuery(StatusQueryInterface, CC5Request wrapper) - Hash builder:
InvumPosGatewayNestPayNestPayHash::ver3()— pure function, byte-pinned tests - Callback controller:
InvumPosGatewayNestPayNestPayCallbackController— REST/wp-json/invum-pos/v1/nestpay/callback(per-bank dispatch via order meta_invum_pos_nestpay_bank)
Mimari kararı (henüz kesin değil): 12 banka için tek WCGateway mi yoksa 12 ayrı WCGateway mi?
- Tek: WC
gateway_id = invum_pos_nestpay, settings'te per-bank
credential setleri. Admin UI'da dropdown ile banka seçilir. Daha az WC menu kalemi, daha temiz UX.
- Çoklu: Her banka kendi WCGateway sınıfı (
InvumPosNestPayAkbank,
…). WC admin'de 12 ayrı entry. Per-bank enable/disable, per-bank başlık. Daha kalabalık ama merchant açısından net.
Önerim: Çoklu — merchant'ın kafasını karıştırmaz, "Akbank" yazınca Akbank-only logo göstermek mümkün. NestPayWCGateway'i abstract base yapıp 12 thin subclass türetilir (per-bank URL + brand label override).
Persist edilen field'lar (transactions tablosu):
transaction_id=<TransId>(NestPay'den dönen)payment_transaction_id= boş (NestPay tek-kalem refund yapıyor;
per-item refund anchor gerekmez)
gateway=nestpay_<bank>(per-bank slug, refund'da banka için
doğru base URL'yi resolve etmek için)
Şu an desteklenmeyen (Faz 1.5+ backlog):
- BIN-bazlı taksit sorgusu (
BINInstallmentQuery) — CP.VPOS'ta zaten
default confirm=false (her banka kendi merchant paneli kuralları)
- Saved cards / vault — NestPay altyapısı saved-card desteklemiyor
(PCI scope SAQ A-EP, kart bizim formumuzda toplandığı için biz kart token'ı saklayamayız direkt; bankalar genelde kendi vault'larını ayrı PSP-as-a-service olarak satıyor)
- Akbank yeni REST API (Faz 2 — ayrı
akbankslug) - 3DS hash'in
ver1/ver2legacy desteği (sadecever3ship)
6. Test connection (Pro modülü)
ProModuleTestConnectionNestPayConnectionTest ne kontrol edecek (planlanan):
- Sandbox
/fim/apiPOST query — basit OrderId araması, response
shape XML mı? <Response> field var mı?
- 3D hash ver3 builder fixture — sabit input → sabit hash output
byte-pin (offline test)
7. Sandbox testi açık sorular (Hafta 17 başında)
- Asseco SEE genel sandbox'a (
entegrasyon.asseco-see.com.tr) tek
credential ile mi, yoksa banka-spesifik test merchant başvurusu ile mi giriliyor?
lang=tryerinelang=en3DS form'unu İngilizce'ye çeviriyor mu
(uluslararası müşteri için)?
- Same-day void için
Type=Voiddenedik diye banka aynıOrderId'i
tekrar sale için kabul eder mi (idempotency)?
- Ziraat ve İşbank kendi sandbox'larında
entegrasyon.asseco-see.com.tr
credential'ı çalışıyor mu, yoksa banka-spesifik istek mi?
- NestPay 13 bankanın hangileri aynı tarihte test edilecek (sprint
prioritization — ilk 3-5 banka pilot)?
- Akbank legacy NestPay endpoint'i hâlâ aktif mi (yoksa sadece eski
merchant'lar için)? Yeni başvuru için akbank slug zorunlu mu?
8. Kaynaklar
- CP.VPOS reference repo: https://github.com/cempehlivan/CP.VPOS
– CP.VPOS/Integrations/Banks/Nestpay/NestpayAbstract.cs — ortak abstract sınıf (Sale/Sale3D/Cancel/Refund/Query) – CP.VPOS/Integrations/Banks/Nestpay/{Akbank,Halkbank,IsBankasi,…}.cs — per-bank URL override (15-18 satır her biri)
- Asseco SEE EST: official dev portal yok — banka temsilcisinden talep edilecek
- Türkçe forum/blog: NestPay 3DS hash sorunları için
entegrasyon.asseco-see.com.trtest deneyimleri (community knowledge — sandbox testi sırasında derlenmeli)
9. Değişiklik geçmişi
| Tarih | Sürüm | Değişiklik | Senkron eden | |————|———————-|—————————————————————————–|————–| | 2026-05-05 | EST/CC5 v1 + ver3 | İlk doldurma — CP.VPOS v2 clean-room reference, 12 banka spec özeti | Claude+Cenk |