Skip to content

Latest commit

 

History

23 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@voxyfy/anadolupay (Node.js / TypeScript)

anadolupay-node

Türk banka ve ödeme sağlayıcıları (NestPay/Asseco, PayFor, PayFlex, GVPS/Garanti, PosNet, iyzico, PayTR, Craftgate, Moka, Tosla, Paratika ve daha fazlası) için tek arayüzlü, framework'e bağımlı olmayan bir ödeme kütüphanesi.

⚠️ Erken aşama. PHP paketindeki (param hariç — TMSF kayyımlığında) tüm banka ve ödeme kuruluşu sürücüleri artık protokol seviyesinde taşındı: fake (sahte), iyzico, tosla, akbank-pos, moka, qnb-payfor, vakifbank, ziraat-payflex, garanti, ziraat-katilim, akbank (NestPay), denizbank, paycell, yapikredi (PosNet), albaraka (PosNet V1), kuveytturk, vakif-katilim, paytr, craftgate, paratika ve tami. Hiçbiri henüz gerçek sandbox/test ortamına karşı ölçülmeditami için ayrıca dokümantasyonun kendi içinde çelişkili olduğu bilinen bir imza sorunu var, bkz. Durum ve Kapsam.

Amacımız

AnadoluPay, Laravel projeleri için 30'dan fazla Türk banka ve ödeme sağlayıcısını tek bir arayüzde toplayan, gerçek banka test ortamlarına karşı ölçülerek doğrulanmış bir PHP paketidir. Bu depo, aynı deneyimi Node.js/TypeScript ekosistemine taşıma girişimidir.

Neden bu işe değer bulduk:

  • Node/TS ekosisteminde bu paketin dengi yok. Gördüğümüz NestPay kütüphaneleri (node-nestpay, node-nestpay-v3 gibi) eski, bakımsız ve tek bir bankayı/protokolü kapsıyor. Onlarca sağlayıcıyı tek arayüzde toplayan, testli bir paket bu ekosistemde henüz yok — yani doldurmaya çalıştığımız boşluk gerçekten var, varsayım değil.
  • Protokolleri sıfırdan öğrenmiyoruz. AnadoluPay'i gerçek banka test ortamlarına karşı (Akbank, İş Bankası, Ziraat, Garanti, QNB, VakıfBank, Kuveyt Türk, iyzico ve daha fazlası) tek tek ölçerek doğrulamıştık — hash sırası, alan adları, 3D Secure akışının dokümanlarda yazmayan gerçek davranışı elimizde.
  • Ama kod çevirmek, protokolü tekrar ölçmek anlamına gelmiyor. PHP tarafında öğrendiğimiz en kalıcı ders şu: bir sürücünün "doğru yazılmış olması" ile "gerçekten bir bankaya karşı çalışması" ayrı şeyler. Bu portta da her sürücüyü ilgili bankanın test ortamına karşı yeniden doğrulayacağız — dil değişse de bu adım atlanmıyor.

Kısacası: PHP tarafındaki mimariyi ve banka bilgisini temel alan, ama Laravel'e değil düz Node.js'e (Express/NestJS/Next.js ile kullanılabilecek şekilde) bağlı, TypeScript-first bir ödeme kütüphanesi kurmaya çalışıyoruz.

Kurulum

npm install @voxyfy/anadolupay

Paket npm'de yayında ve CommonJS/ESM ikisini de destekler:

// ESM / TypeScript
import { createAnadoluPay, FakeGateway } from '@voxyfy/anadolupay';
// CommonJS
const { createAnadoluPay, FakeGateway } = require('@voxyfy/anadolupay');

Node.js 18 veya üzeri gerekir (fetch global olarak kullanılır, ek bir HTTP istemci bağımlılığı yoktur). Kütüphane framework'e bağımlı değildir; hangi sürücüleri hangi kimlik bilgileriyle kuracağınızı createAnadoluPay({ drivers }) çağrısında siz belirlersiniz — aşağıdaki sürücü örneklerine bakın.

İlgili projeler

  • Voxyfy/anadolupay — bu paketin taşındığı kaynak: Laravel için PHP ödeme kütüphanesi. Desteklenen bankaların tam listesi, doğrulama durumu ve protokol ayrıntıları için oradaki README'ye bakın.
  • Voxyfy/anadolupay-laravel — PHP paketinin gerçek banka test ortamlarına karşı denendiği örnek Laravel projesi. Her sürücünün 3D Secure akışı tarayıcıdan burada koşturulup ölçüldü.
  • Voxyfy/anadolupay-node-example — bu paketin Express + React ile hazırlanmış örnek test projesi; yukarıdaki Laravel örneğinin Node.js karşılığı. Aynı .env değişken adlarını kullanır.
  • Voxyfy/anadoluship (npm) — aynı driver mimarisinin kargo firmaları (MNG, UPS, Yurtiçi, Aras, PTT, Sürat) için Node.js/TypeScript karşılığı.
  • Voxyfy/anadolushield (npm) — LLM API'lerine göndermeden önce TCKN/VKN/IBAN/isim gibi kişisel verileri maskeleyen, KVKK riskini azaltan bağımsız bir kütüphane.
  • Voxyfy/anadolucookie — KVKK/GDPR uyumlu, framework'e bağımlı olmayan çerez rıza (cookie consent) banner kütüphanesi.

Mimari

PHP/Laravel mimarisiyle eşleşme, ama Laravel'e (facade, service container, config()) bağımlı olmadan:

PHP/Laravel Node/TS karşılığı
PaymentGatewayInterface + Supports* contract'ları PaymentGateway interface'i + contracts/capabilities.ts'teki tip-koruyucu (supportsCancellation(gateway)) fonksiyonları — TS'te interface'ler runtime'da olmadığı için instanceof yerine bunlar kullanılır
DTO'lar (CreatePaymentData, PaymentResponse, …) Aynı adlarla TS sınıfları, dto/ altında
config('anadolupay.banks') / Laravel service container createAnadoluPay({ drivers }) — tipli bir fabrika, framework'e bağımlı değil
Support/Money.php support/Money.ts — kuruş cinsinden tam sayı, float aritmetiği yok
Gateways/Bank/AbstractBankGateway.php gateways/bank/AbstractBankGateway.ts — aynı şablon-metot akışı (createPayment/verify/refund + soyut kancalar); Laravel'e özgü event yayını ve mükerrer-ödeme koruması bilerek yok
Support/Bank/BankConfig.php, BankHttpClient.php support/bank/BankConfig.ts, BankHttpClient.ts
Pest (405 test) Vitest (261 test)

İlk sürücü FakeGateway — ağ çağrısı yapmaz, işlemleri bellekte tutar. Bilerek en başta seçildi: mimarinin (DTO'lar, contract'lar, yetenek tespiti, hata hiyerarşisi) doğru kurulduğunu kanıtlar ve gerçek bir banka kimliği gerektirmez.

import { createAnadoluPay, FakeGateway, CreatePaymentData } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    fake: () => new FakeGateway(),
  },
});

const gateway = anadolupay.driver('fake');

const payment = await gateway.createPayment(
  new CreatePaymentData({
    amount: 100,
    currency: 'TRY',
    orderId: 'SIPARIS-123',  // boş bırakılırsa ön ekle üretilir
    customer: {},
  }),
);

İkinci sürücü IyzicoGateway — PHP tarafındaki IyzicoGateway + IyzicoHttpClient (IYZWSv2 kimlik doğrulama) + IyzicoMapper + IyzicoSignatureValidator'ın (HMAC-SHA256 imza şeması) birebir TS karşılığı. 3DS başlatma, callback/webhook doğrulama, iade, durum sorgusu, BIN sorgusu ve taksit sorgusunu kapsıyor.

import { createAnadoluPay, IyzicoGateway } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    iyzico: () =>
      new IyzicoGateway({
        baseUrl: process.env.IYZICO_BASE_URL!,
        apiKey: process.env.IYZICO_API_KEY!,
        secretKey: process.env.IYZICO_SECRET_KEY!,
        defaultCallbackUrl: process.env.IYZICO_CALLBACK_URL,
      }),
  },
});

Üçüncü sürücü ToslaGateway — ilk "banka ailesi" sürücüsü. PHP'de Tosla, NestPay/PayFor gibi bankalarla aynı şablon-metot tabanını (AbstractBankGateway) paylaşıyordu; bu yüzden Tosla ile birlikte o tabanı da taşıdık (AbstractBankGateway, BankConfig, BankHttpClient). NestPay/PayFor/PayFlex gibi sonraki sürücüler bu tabanı genişletecek.

import { createAnadoluPay, ToslaGateway } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    tosla: () =>
      new ToslaGateway({
        merchantId: process.env.TOSLA_MERCHANT_ID!,
        username: process.env.TOSLA_USERNAME!,
        secretKey: process.env.TOSLA_SECRET_KEY!,
        endpoints: {
          payment_api: process.env.TOSLA_PAYMENT_API!,
          gateway_3d: process.env.TOSLA_GATEWAY_3D!,
        },
      }),
  },
});

Dördüncü sürücü AkbankPosGateway — Akbank'ın yeni JSON tabanlı sanal POS API'si (Asseco tabanlı eski akbank sürücüsünden farklı, ayrı bir protokol). AbstractBankGateway'i genişletiyor; kendine özgü kısmı auth-hash başlığı ile gövdenin tamamının imzalanması — bu yüzden BankHttpClient'a genel bir send(url, body, headers) metodu eklendi (postJson da artık onu kullanıyor).

import { createAnadoluPay, AkbankPosGateway } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    'akbank-pos': () =>
      new AkbankPosGateway({
        merchantId: process.env.AKBANK_POS_MERCHANT_SAFE_ID!,
        terminalId: process.env.AKBANK_POS_TERMINAL_SAFE_ID!,
        secretKey: process.env.AKBANK_POS_SECRET_KEY!,
        endpoints: {
          payment_api: process.env.AKBANK_POS_PAYMENT_API!,
          gateway_3d: process.env.AKBANK_POS_GATEWAY_3D!,
          gateway_3d_host: process.env.AKBANK_POS_GATEWAY_3D_HOST!,
        },
      }),
  },
});

Beşinci sürücü MokaGateway — üçüncü "banka ailesi" sürücüsü. Moka'nın en tuhaf tarafı: 3D dönüşünde başarı/başarısızlık ayrı bir alanda bildirilmez, CodeForHash değerine T/F eklenip sha256'sı alınır ve sonuç hashValue olarak karşılaştırılır — bu yüzden verify()'ı override edip sipariş bağlamından (order.code_for_hash) gelen değeri callback yüküne enjekte ediyor, sonra temel akışı çağırıyor.

import { createAnadoluPay, MokaGateway, VerifyPaymentData } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    moka: () =>
      new MokaGateway({
        merchantId: process.env.MOKA_DEALER_CODE!,
        username: process.env.MOKA_USERNAME!,
        password: process.env.MOKA_PASSWORD!,
        endpoints: { payment_api: process.env.MOKA_PAYMENT_API! },
      }),
  },
});

const payment = await anadolupay.driver('moka').createPayment(/* ... */);
const codeForHash = payment.raw['code_for_hash']; // siparişle birlikte saklayın

// Callback geldiğinde:
await anadolupay.driver('moka').verify(
  new VerifyPaymentData({ payload: callbackBody, order: { code_for_hash: codeForHash } }),
);

Altıncı sürücü PayForGateway (qnb-payfor) — ilk XML tabanlı sürücü. Bununla birlikte Xml yardımcı modülü (fast-xml-parser ile encode/decode) ve BankHttpClient.postXml() eklendi. PayFor sınıfı generiktir (PHP tarafında da öyleydi): bank anahtarını constructor'a parametre olarak alır, böylece aynı sınıf ziraat-katilim gibi diğer PayFor tabanlı bankalar için de kullanılabilir.

import { createAnadoluPay, PayForGateway } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    'qnb-payfor': () =>
      new PayForGateway('qnb-payfor', {
        merchantId: process.env.QNB_PAYFOR_MERCHANT_ID!,
        username: process.env.QNB_PAYFOR_USERNAME!,
        password: process.env.QNB_PAYFOR_PASSWORD!,
        secretKey: process.env.QNB_PAYFOR_SECRET_KEY!,
        extra: { mbr_id: process.env.QNB_PAYFOR_MBR_ID ?? '5' },
        endpoints: {
          payment_api: process.env.QNB_PAYFOR_PAYMENT_API!,
          gateway_3d: process.env.QNB_PAYFOR_GATEWAY_3D!,
          gateway_3d_host: process.env.QNB_PAYFOR_GATEWAY_3D_HOST!,
        },
      }),
  },
});

Xml'in bilinen sınırı: encode()'daki encoding parametresi şu an yalnızca UTF-8 için tam doğru çalışıyor. PHP tarafı ISO-8859-9 gibi kodlamalarda gövdeyi bayt dizisine çeviriyordu; bu port henüz o adımı (Node'da Buffer gövde desteği) eklemedi — NestPay ailesi taşınırken bu netleşecek.

Yedinci sürücü PayFlexGateway (vakifbank) — VakıfBank/Ziraat/İş Bankası'nın PayFlex V4 (MPI VPOS) altyapısı. İki yeni ihtiyaç bununla geldi:

  • BankHttpClient.postForm() — PayFlex, XML'i JSON/XML gövdesi olarak değil, prmstr adlı tek bir application/x-www-form-urlencoded alanının içinde bekler.
  • İki aşamalı 3D akışı — kart önce bankaya değil MPI'ya (Enrollment.aspx) gönderilir; kartı çıkaran bankanın ACS adresi ve PaReq/MD geri döner, 3D formu o adrese POST edilir. Bazı kurulumlarda (BKM GO) PaReq düz bir 3DS bloğu değil, kendi kendini gönderen base64 kodlu bir HTML sayfasıdır — bu port o sayfayı regex'le ayrıştırıp gerçek form hedefini çıkarıyor (PHP'deki davranışın aynısı).
import { createAnadoluPay, PayFlexGateway } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    vakifbank: () =>
      new PayFlexGateway('vakifbank', {
        merchantId: process.env.VAKIFBANK_MERCHANT_ID!,
        password: process.env.VAKIFBANK_PASSWORD!,
        terminalId: process.env.VAKIFBANK_TERMINAL_ID!,
        endpoints: {
          payment_api: process.env.VAKIFBANK_PAYMENT_API!,
          gateway_3d: process.env.VAKIFBANK_GATEWAY_3D!,
          query_api: process.env.VAKIFBANK_QUERY_API!,
        },
      }),
  },
});

PayFlexGateway jeneriktir — PHP'de olduğu gibi bank adı constructor parametresi. Bu sayede ziraat-payflex için hiç yeni kod yazmadan, sadece farklı kimlik/uçlarla aynı sınıf kullanılıyor:

'ziraat-payflex': () =>
  new PayFlexGateway('ziraat-payflex', {
    merchantId: process.env.ZIRAAT_PAYFLEX_MERCHANT_ID!,
    password: process.env.ZIRAAT_PAYFLEX_PASSWORD!,
    terminalId: process.env.ZIRAAT_PAYFLEX_TERMINAL_ID!,
    endpoints: {
      payment_api: process.env.ZIRAAT_PAYFLEX_PAYMENT_API!,
      gateway_3d: process.env.ZIRAAT_PAYFLEX_GATEWAY_3D!,
      query_api: process.env.ZIRAAT_PAYFLEX_QUERY_API!,
    },
  }),

Sekizinci sürücü GarantiGateway — standart akışa dönen (Tosla/Moka/ PayFlex gibi createPayment/verify'ı override etmiyor) ikinci XML tabanlı sürücü. Kendine özgü noktaları: tutarlar kuruş cinsinden tam sayı, imzalar sha512/sha1 büyük harf hex, ve securityData (şifre + 9 haneye sıfır dolgulu terminal no) — iade/iptalde ayrı bir refund_username kullanabiliyor (extra.refund_username).

Üye işyeri 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 ile reddedilir. extra.sub_merchant_id verildiğinde alan provizyon, 3D form, provizyon kapama, iptal/iade ve sorgu isteklerine eklenir — boş bırakılırsa istekler alanı hiç içermez ve imza 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 extra.sub_merchant_id_path ile taşınabilir.

Bankanın bayi tanımında her bayinin kullanacağı kart numaraları da tanımlanır: gönderilen kart o bayi altında kayıtlı değilse işlem reddedilir.

import { createAnadoluPay, GarantiGateway } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    garanti: () =>
      new GarantiGateway({
        merchantId: process.env.GARANTI_MERCHANT_ID!,
        terminalId: process.env.GARANTI_TERMINAL_ID!,
        username: process.env.GARANTI_USERNAME!,
        password: process.env.GARANTI_PASSWORD!,
        secretKey: process.env.GARANTI_SECRET_KEY!,
        refundPassword: process.env.GARANTI_REFUND_PASSWORD,
        extra: {
          refund_username: process.env.GARANTI_REFUND_USERNAME,
          // Yalnızca bayi yapılandırmalı terminaller için.
          sub_merchant_id: process.env.GARANTI_SUB_MERCHANT_ID,
        },
        endpoints: {
          payment_api: process.env.GARANTI_PAYMENT_API!,
          gateway_3d: process.env.GARANTI_GATEWAY_3D!,
        },
      }),
  },
});

PayForGateway de jeneriktirziraat-katilim için yine yeni kod gerekmedi, sadece farklı kimlik/uçlarla. Tek fark: Ziraat Katılım'ın dönüş hash'i tutarsız üretildiği için PHP'de olduğu gibi verifyHash varsayılan olarak false önerilir:

'ziraat-katilim': () =>
  new PayForGateway('ziraat-katilim', {
    merchantId: process.env.ZIRAAT_KATILIM_MERCHANT_ID!,
    username: process.env.ZIRAAT_KATILIM_USERNAME!,
    password: process.env.ZIRAAT_KATILIM_PASSWORD!,
    secretKey: process.env.ZIRAAT_KATILIM_SECRET_KEY!,
    verifyHash: process.env.ZIRAAT_KATILIM_VERIFY_HASH === 'true',
    endpoints: {
      payment_api: process.env.ZIRAAT_KATILIM_PAYMENT_API!,
      gateway_3d: process.env.ZIRAAT_KATILIM_GATEWAY_3D!,
    },
  }),

Dokuzuncu sürücü AssecoGateway (akbank, NestPay/EST ailesi) — roadmap'te en başından beklettiğimiz parça, çünkü NestPay gövdeyi ISO-8859-9 ister. Bu, bu porttaki en köklü altyapı değişikliğiyle geldi:

  • Xml.encodeBytes() / Xml.decodeBytes()encode()/decode() hâlâ UTF-8 dizgisiyle çalışır (diğer sürücüler değişmeden çalışmaya devam eder); yeni metotlar iconv-lite ile gerçek bayt dizisi (Buffer) üretir/okur.
  • BankHttpClient.postXml() artık gövdeyi Buffer olarak gönderir ve yanıtı da response.arrayBuffer() üzerinden aynı kodlamayla çözümler — response.text() her zaman UTF-8 varsaydığı için Türkçe karakterleri (İ, Ş, Ğ, ı, ü, ö, ç) bozuyordu. Bunu bir testle kanıtladık: İşyeri gibi bir alanın isteğe tam olarak ISO-8859-9 baytlarıyla gittiğini ve yanıttaki Türkçe karakterlerin bozulmadan geri okunduğunu doğruluyor.
  • Diğer XML tabanlı sürücüler (PayForGateway, PayFlexGateway, GarantiGateway) UTF-8 kullandığı için davranışları değişmedi — regresyon testleriyle doğrulandı.

AssecoGateway de jeneriktir; aynı sınıf isbank, ziraat, halkbank, qnb, teb, sekerbank, ing, alternatifbank için de kullanılabilir.

import { createAnadoluPay, AssecoGateway } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    akbank: () =>
      new AssecoGateway('akbank', {
        merchantId: process.env.AKBANK_MERCHANT_ID!,
        username: process.env.AKBANK_USERNAME!,
        password: process.env.AKBANK_PASSWORD!,
        secretKey: process.env.AKBANK_SECRET_KEY!,
        endpoints: {
          payment_api: process.env.AKBANK_PAYMENT_API!,
          gateway_3d: process.env.AKBANK_GATEWAY_3D!,
        },
      }),
  },
});

Onuncu ve on birinci sürücüler InterPosGateway (denizbank) ve PaycellGateway — ikisi de standart olmayan akışlarda.

  • InterPosGateway, form tabanlı (XML değil) düz postForm istekleri kullanır; standart createPayment/verify akışını override etmez.
  • PaycellGateway, Tosla/Moka/PayFlex gibi createPayment/verify'ı tamamen override eder: kart bilgisi ödeme ucuna hiç gitmez, önce ayrı bir uçtan (token_api) cardToken alınır, sonra 3D oturumu açılır. İmzası iki aşamalıdır ve tamamı büyük harfe çevrilerek hesaplanır — bu adım atlanırsa imza hiçbir zaman tutmaz.

Bu ikisini taşırken gerçek bir hata bulup düzelttik: BankHttpClient.decode(), PHP'deki parse_str() geri dönüşünü (banka JSON/XML değil düz key=value&... query-string döndürdüğünde) hiç portlamamıştı — InterPos'un form-tabanlı yanıtları bu yüzden "çözümlenemedi" hatasıyla patlıyordu. Şimdi PHP'nin tam sırasını izliyor: JSON → XML → (2xx değilse hata) → query-string → { raw_body }.

import { createAnadoluPay, InterPosGateway, PaycellGateway } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    denizbank: () =>
      new InterPosGateway('denizbank', {
        merchantId: process.env.DENIZBANK_MERCHANT_ID!,
        username: process.env.DENIZBANK_USERNAME!,
        password: process.env.DENIZBANK_PASSWORD!,
        secretKey: process.env.DENIZBANK_SECRET_KEY!,
        endpoints: {
          payment_api: process.env.DENIZBANK_PAYMENT_API!,
          gateway_3d: process.env.DENIZBANK_GATEWAY_3D!,
          gateway_3d_host: process.env.DENIZBANK_GATEWAY_3D_HOST!,
        },
      }),
    paycell: () =>
      new PaycellGateway({
        merchantId: process.env.PAYCELL_MERCHANT_ID!,
        username: process.env.PAYCELL_USERNAME!,
        password: process.env.PAYCELL_PASSWORD!,
        secretKey: process.env.PAYCELL_SECRET_KEY!,
        extra: { msisdn: process.env.PAYCELL_MSISDN },
        endpoints: {
          payment_api: process.env.PAYCELL_PAYMENT_API!,
          token_api: process.env.PAYCELL_TOKEN_API!,
          gateway_3d: process.env.PAYCELL_GATEWAY_3D!,
        },
      }),
  },
});

On ikinci sürücü PosNetGateway (yapikredi) — bugüne kadarki en katmanlı 3D akışı. PosNet doğrulamayı üç sunucu isteğine yayar:

  1. oosRequestData — kart ve sipariş bilgisi bankaya gönderilir, banka 3D formunda kullanılacak data1/data2/sign paketlerini döner.
  2. Bu paketler 3D geçidine POST edilir, müşteri kimlik doğrular.
  3. Dönüşteki MerchantPacket/BankPacket/Sign üçlüsü oosResolveMerchantData ile çözülür, mac doğrulanır ve oosTranData ile provizyon tamamlanır — bu yüzden verify() (Moka/PayFlex/Paycell gibi) tamamen override edilir.

Bununla birlikte gelen yeni ihtiyaç: BankHttpClient.postXmlAsFormField(). PosNet, XML'i postXml()'deki gibi gövdenin tamamı olarak değil, xmldata adlı tek bir form alanının değeri olarak ister — ama bu değerin ISO-8859-9 baytları bozulmadan gitmesi gerekir. URLSearchParams burada kullanılamaz (JS string'ini kodlamadan önce UTF-8'e çevirir); bunun yerine XML baytları doğrudan, byte-safe bir yüzde-kodlamayla (percentEncodeBytes()) forma yazılıyor.

PosNet'in diğer kendine özgü noktaları: para birimini ISO sayısal kodla değil kendi iki harfli kısaltmasıyla ister (TL/US/EU — sayısal kod gönderildiğinde E190 CurrencyCode hatalı döner), zorunlu bir extra.posnet_id alanı vardır, ve iade/iptal/provizyon-kapama işlemleri sipariş numarası yerine mümkünse hostLogKey ile eşlenir (yoksa 24 haneli, 3D siparişlerde TDSC önekli sipariş numarasına düşer).

import { createAnadoluPay, PosNetGateway } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    yapikredi: () =>
      new PosNetGateway({
        merchantId: process.env.YAPIKREDI_MERCHANT_ID!,
        terminalId: process.env.YAPIKREDI_TERMINAL_ID!,
        secretKey: process.env.YAPIKREDI_SECRET_KEY!,
        extra: { posnet_id: process.env.YAPIKREDI_POSNET_ID! },
        endpoints: {
          payment_api: process.env.YAPIKREDI_PAYMENT_API!,
          gateway_3d: process.env.YAPIKREDI_GATEWAY_3D!,
        },
      }),
  },
});

On üçüncü sürücü PosNetV1Gateway (albaraka) — PosNet'in JSON tabanlı yeni sürümü. Yapı Kredi'nin XML tabanlı PosNetGateway'inden farklı olarak paket (data1/data2/sign) mekanizması yoktur; standart createPayment/ verify akışını kullanır (Garanti/Asseco gibi, override gerekmez). Her istek MacParams/MACParams alanında MAC hesabına giren alan adlarını iki nokta ile bildirir (sha256_b64, ayraçsız).

import { createAnadoluPay, PosNetV1Gateway } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    albaraka: () =>
      new PosNetV1Gateway({
        merchantId: process.env.ALBARAKA_MERCHANT_ID!,
        terminalId: process.env.ALBARAKA_TERMINAL_ID!,
        secretKey: process.env.ALBARAKA_SECRET_KEY!,
        extra: { posnet_id: process.env.ALBARAKA_POSNET_ID! },
        endpoints: {
          payment_api: process.env.ALBARAKA_PAYMENT_API!,
          gateway_3d: process.env.ALBARAKA_GATEWAY_3D!,
        },
      }),
  },
});

On dördüncü ve on beşinci sürücüler KuveytPosGateway (kuveytturk) ve VakifKatilimGateway (vakif-katilim) — BOA/TDV2.0 protokolü. İkisi de aynı imza şemasını (sha1_b64, aynı alan sırası) paylaşır ama PHP tarafında da ayrı sınıflardır (kod tekrarı var, ortak bir taban sınıf yok); bu port da aynı şekilde ayrı tutar. İkisinin de akışı standart akıştan sapar, bu yüzden createPayment/verify tamamen override edilir:

  • 3D adımında banka form alanları değil, doğrudan tarayıcıya basılacak bir HTML sayfası döner (PaymentResponse.htmlContent) — bunun için BankHttpClient.postXmlForRawBody() eklendi: postXml()'den farkı, yanıtı JSON/XML olarak çözümlemeden ham dizgi olarak dönmesi.
  • Dönüşteki AuthenticationResponse alanı URL kodlanmış bir XML belgesidir; önce çözülür, sonra provizyon isteği yapılır.
  • Kuveyt Türk sorgu/iade/iptali ayrı bir BOA servisinden (query_api) yürütür; Vakıf Katılım hepsini payment_api üzerinden yapar.
import { createAnadoluPay, KuveytPosGateway, VakifKatilimGateway } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    kuveytturk: () =>
      new KuveytPosGateway({
        merchantId: process.env.KUVEYTTURK_MERCHANT_ID!,
        username: process.env.KUVEYTTURK_USERNAME!,
        secretKey: process.env.KUVEYTTURK_SECRET_KEY!,
        extra: { customer_id: process.env.KUVEYTTURK_CUSTOMER_ID },
        endpoints: {
          payment_api: process.env.KUVEYTTURK_PAYMENT_API!,
          query_api: process.env.KUVEYTTURK_QUERY_API!,
        },
      }),
    'vakif-katilim': () =>
      new VakifKatilimGateway({
        merchantId: process.env.VAKIF_KATILIM_MERCHANT_ID!,
        username: process.env.VAKIF_KATILIM_USERNAME!,
        secretKey: process.env.VAKIF_KATILIM_SECRET_KEY!,
        extra: {
          customer_id: process.env.VAKIF_KATILIM_CUSTOMER_ID,
          sub_merchant_id: process.env.VAKIF_KATILIM_SUB_MERCHANT_ID ?? '0',
        },
        endpoints: {
          payment_api: process.env.VAKIF_KATILIM_PAYMENT_API!,
          gateway_3d_host: process.env.VAKIF_KATILIM_GATEWAY_3D_HOST!,
        },
      }),
  },
});

On altıncı, on yedinci ve on sekizinci sürücüler — ilk üç ödeme kuruluşu sürücüsü, artık bir bankanın sanal POS'u değil, birden çok bankayı tek API arkasında toplayan platformlar:

  • PayTrGateway (paytr) — standart akışı kullanır. İmza hmac_sha256_b64; bildirim (webhook) doğrulaması düz metin OK yanıtı bekler (ProvidesWebhookAcknowledgement), aksi hâlde PayTR bildirimi saatlerce yeniden gönderir. İptal (void) desteklemez, yalnızca iade eder.
  • CraftgateGateway (craftgate) — createPayment() tamamen override edilir: 3D adımı form POST değil, hazır bir HTML sayfası (base64) döner. API imzası (x-signature, gövde baytları üzerinden) ile 3D dönüş imzası (ayrı bir 3D Secure Callback Key, sha256_hex + ### ayracı) birbirinden tamamen farklı anahtar/algoritma kullanır — bu ikisini karıştırmak "Signature is not equal!" hatasının en yaygın sebebidir.
  • ParatikaGateway (paratika) — form-encoded POST, istek imzası yok (kimlik doğrulama üç düz alanla: MERCHANT/MERCHANTUSER/ MERCHANTPASSWORD); yalnızca 3D dönüşü imzalı. Akış her modelde bir SESSIONTOKEN ile başladığı için createPayment() tamamen override edilir. Dönüş imzasında bilinen bir tuzak var: SD_SHA512 dokümanda "Deprecated / Legacy — Do not use!" ama örnek yanıtlarda önce göründüğü için yanlışlıkla kullanılıyor; bu sürücü güncel olan sdSha512'yi doğrular.
import { createAnadoluPay, CraftgateGateway, ParatikaGateway, PayTrGateway } from '@voxyfy/anadolupay';

const anadolupay = createAnadoluPay({
  drivers: {
    paytr: () =>
      new PayTrGateway({
        secretKey: process.env.PAYTR_MERCHANT_KEY!,
        password: process.env.PAYTR_MERCHANT_SALT!,
        merchantId: process.env.PAYTR_MERCHANT_ID!,
        endpoints: {
          payment_api: process.env.PAYTR_PAYMENT_API!,
          gateway_3d: process.env.PAYTR_GATEWAY_3D!,
        },
      }),
    craftgate: () =>
      new CraftgateGateway({
        username: process.env.CRAFTGATE_API_KEY!,
        secretKey: process.env.CRAFTGATE_SECRET_KEY!,
        password: process.env.CRAFTGATE_CALLBACK_KEY!,
        extra: { merchant_hook_key: process.env.CRAFTGATE_HOOK_KEY },
        endpoints: { payment_api: process.env.CRAFTGATE_PAYMENT_API! },
      }),
    paratika: () =>
      new ParatikaGateway({
        merchantId: process.env.PARATIKA_MERCHANT!,
        username: process.env.PARATIKA_MERCHANT_USER!,
        password: process.env.PARATIKA_MERCHANT_PASSWORD!,
        secretKey: process.env.PARATIKA_SECRET_KEY!,
        endpoints: {
          payment_api: process.env.PARATIKA_PAYMENT_API!,
          gateway_3d: process.env.PARATIKA_GATEWAY_3D!,
          gateway_3d_auth: process.env.PARATIKA_GATEWAY_3D_AUTH!,
          gateway_3d_host: process.env.PARATIKA_GATEWAY_3D_HOST!,
        },
      }),
  },
});

Sipariş numarası

Sipariş numarasını kendiniz veriyorsanız hiçbir şey değişmez. Vermek istemiyorsanız orderId'yi boş bırakın; numara ön ekiyle birlikte üretilir:

ANADOLUPAY_ORDER_PREFIX=ODM-
ANADOLUPAY_ORDER_LENGTH=10        # rastgele bölümün uzunluğu, en az 6
new CreatePaymentData({ amount: 199.9, currency: 'TRY', customer: {} });
// orderId → ODM-4KX9AB2Q7T

anadolupay.orderId();                 // ödemeyi başlatmadan önce gerekirse
makeOrderNumber({ prefix: 'ODM-' });  // istemciye ihtiyaç duymadan

Ön eki ortam değişkeni yerine kod içinde vermek isterseniz istemciye geçirin; açık seçenek ortam değişkenini ezer:

const anadolupay = createAnadoluPay({
  drivers: { ... },
  order: { prefix: 'ODM-', length: 10 },
});

Bu, paketin ortam değişkeni okuduğu tek yerdir; değişken tanımlı değilse davranış değişmez (ön eksiz, 10 karakter). CreatePaymentData içindeki otomatik üretim istemciyi görmediği için yalnızca ortam değişkenine bakar — ön eki createAnadoluPay({ order }) ile veriyorsanız numarayı anadolupay.orderId() ile üretip DTO'ya geçirin.

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.

Durum

Alan Durum
Mimari (DTO'lar, contract'lar, hata hiyerarşisi, Money) ✅ Kuruldu, tip kontrolünden ve testlerden geçiyor
FakeGateway ✅ Çalışıyor, testli
IyzicoGateway ⚠️ Protokol taşındı, mock fetch ile birim testlerden geçiyor — gerçek iyzico sandbox'ına karşı henüz ölçülmedi. PHP tarafında öğrenilen ders burada da geçerli: kodun doğru yazılmış olması, bankaya/sağlayıcıya karşı çalıştığı anlamına gelmez
AbstractBankGateway + ToslaGateway ⚠️ Şablon-metot tabanı ve Tosla protokolü taşındı, birim testlerden geçiyor — gerçek Tosla test ortamına karşı henüz ölçülmedi. Not: Laravel'e özgü event yayını ve mükerrer-ödeme koruması bilerek bu portta yok (bkz. Mimari)
AkbankPosGateway ⚠️ Protokol taşındı, birim testlerden geçiyor — gerçek Akbank test ortamına karşı henüz ölçülmedi
MokaGateway ⚠️ Protokol taşındı, birim testlerden geçiyor — gerçek Moka test servisine karşı henüz ölçülmedi
Xml + BankHttpClient.postXml() + PayForGateway ⚠️ İlk XML tabanlı sürücü taşındı, birim testlerden geçiyor — gerçek QNB PayFor demo ortamına karşı henüz ölçülmedi. encoding parametresi şimdilik yalnızca UTF-8 için doğru (bkz. Mimari)
BankHttpClient.postForm() + PayFlexGateway ⚠️ İki aşamalı MPI/ACS akışı ve prmstr form gövdesi taşındı, birim testlerden geçiyor — gerçek VakıfBank sandbox'ına karşı henüz ölçülmedi
GarantiGateway ⚠️ Protokol taşındı, birim testlerden geçiyor — gerçek Garanti test terminaline karşı henüz ölçülmedi
Xml/BankHttpClient ISO-8859-9 desteği + AssecoGateway (NestPay) ✅ Gerçek bayt-dizisi encode/decode çalışıyor (testle kanıtlandı), diğer XML sürücülerinde regresyon yok — ⚠️ ama AssecoGateway'in kendisi gerçek bir NestPay bankasına karşı henüz ölçülmedi
InterPosGateway (denizbank) + BankHttpClient.decode() query-string fallback'i ✅ Fallback eklendi ve testlerle kanıtlandı — ⚠️ InterPosGateway'in kendisi gerçek DenizBank test ortamına karşı henüz ölçülmedi
PaycellGateway ⚠️ İki aşamalı büyük-harf hash + kart token + 3D oturum akışı taşındı, birim testlerden geçiyor — gerçek Paycell test ortamına karşı henüz ölçülmedi
BankHttpClient.postXmlAsFormField() + PosNetGateway (yapikredi) ⚠️ Üç adımlı oosRequestData/oosResolveMerchantData/oosTranData akışı ve byte-safe form-alanı yüzde-kodlaması taşındı, birim testlerden geçiyor — gerçek Yapı Kredi PosNet test ortamına karşı henüz ölçülmedi
PosNetV1Gateway (albaraka) ⚠️ Protokol taşındı, birim testlerden geçiyor — gerçek Albaraka Türk test ortamına karşı henüz ölçülmedi
BankHttpClient.postXmlForRawBody() + KuveytPosGateway/VakifKatilimGateway ⚠️ BOA/TDV2.0 (hazır HTML dönen 3D akışı + URL kodlu AuthenticationResponse çözümleme) taşındı, birim testlerden geçiyor — gerçek Kuveyt Türk/Vakıf Katılım test ortamına karşı henüz ölçülmedi. Kuveyt Türk'ün sorgu/iade/iptal servisi (query_api) PHP tarafında WCF basicHttpBinding uyumsuzluğuyla ölçülmüştü — bu port da o bilinen sınırı taşıyor
PayTrGateway ⚠️ Protokol taşındı, birim testlerden geçiyor — gerçek PayTR test ortamına karşı henüz ölçülmedi
BankHttpClient.get() + CraftgateGateway ⚠️ İlk ödeme orkestrasyonu sürücüsü (banka değil, PSP); createPayment() tamamen override edildi. Birim testlerden geçiyor — gerçek Craftgate sandbox'ına karşı henüz ölçülmedi
ParatikaGateway ⚠️ Oturum anahtarlı (SESSIONTOKEN) üç modelli akış taşındı, birim testlerden geçiyor — gerçek Paratika test ortamına karşı henüz ölçülmedi
npm yayını @voxyfy/anadolupay yayında

Bu paketi PHP'den tek seferde değil, sürücü sürücü taşıyoruz; sürüm numarası bu yüzden 0.1.0'dan başlayıp 1.0.0'a atlamış, en son 1.4.2'ye ulaşmıştır (aradaki bazı sürücü ekleri henüz yayınlanmadığı için tek bir sürümde toplandı; 1.4.0 sürücü değil sipariş numarası üretimini, 1.4.1 Garanti bayi kodunu ekler, 1.4.2 bayi kodunun yazıldığı düğümü banka dokümanına göre Terminal içine düzeltir). param hariç (TMSF kayyımlığında, aktif sürdürülmüyor) PHP paketindeki tüm sürücüler artık protokol seviyesinde taşındı. Aşağıdaki tablo hangi sürümde hangi sürücünün eklendiğini değil, şu anki kapsamı gösterir.

Kapsam (banka ve ödeme kuruluşları)

PHP paketindeki (Voxyfy/anadolupay) tam liste, hangilerinin bu depoya taşındığını gösteren bir sütunla. "Node taşıması" sütunu bu deponun durumudur; "PHP'de doğrulama" sütunu ise kaynak pakette o sürücünün gerçek bir banka/sağlayıcıya karşı ne kadar ölçüldüğünü özetler (ayrıntı için PHP README'sindeki Doğrulama durumu tablosuna bakın).

Bankalar

Driver Banka Altyapı Node taşıması PHP'de doğrulama
akbank Akbank (NestPay) Asseco / Payten Taşındı (AssecoGateway jenerik — bkz. Durum) Uçtan uca
isbank İş Bankası Asseco / Payten Taşındı (aynı sınıf, farklı kimlikle test edildi) Uçtan uca
ziraat Ziraat Bankası (NestPay) Asseco / Payten Taşındı (aynı sınıf) Uçtan uca
halkbank Halkbank Asseco / Payten Taşındı (aynı sınıf) Dokümana göre — test kimliği bekleniyor
qnb QNB Finansbank (NestPay) Asseco / Payten Taşındı (aynı sınıf) Kısmen — 3D geçti, provizyona yetkisiz
teb TEB Asseco / Payten Taşındı (aynı sınıf) Dokümana göre — test kimliği bekleniyor
sekerbank Şekerbank Asseco / Payten Taşındı (aynı sınıf) Dokümana göre — test kimliği bekleniyor
ing ING Asseco / Payten Taşındı (aynı sınıf) Dokümana göre — test kimliği bekleniyor
alternatifbank Alternatif Bank Asseco / Payten Taşındı (aynı sınıf) Dokümana göre — test kimliği bekleniyor
turkiyefinans Türkiye Finans Asseco / Payten Taşındı (aynı sınıf) Kısmen — 3D geçti, provizyona yetkisiz
garanti Garanti BBVA GVPS Taşındı (protokol seviyesinde — bkz. Durum) Uçtan uca
yapikredi Yapı Kredi PosNet (XML) Taşındı (protokol seviyesinde — bkz. Durum) Kısmen
albaraka Albaraka Türk PosNet V1 (JSON) Taşındı (PosNetV1Gateway, protokol seviyesinde — bkz. Durum) Test erişimi bekleniyor
vakifbank VakıfBank PayFlex V4 Taşındı (protokol seviyesinde — bkz. Durum) Uçtan uca
ziraat-payflex Ziraat Bankası (PayFlex) PayFlex V4 Taşındı (PayFlexGateway jenerik, aynı sınıf farklı kimlikle test edildi) Kısmen
denizbank DenizBank InterPos Taşındı (protokol seviyesinde — bkz. Durum) Dokümana göre — IP kısıtlı
qnb-payfor QNB Finansbank / Enpara (PayFor) PayFor Taşındı (protokol seviyesinde — bkz. Durum) Uçtan uca
ziraat-katilim Ziraat Katılım PayFor Taşındı (PayForGateway jenerik, aynı sınıf farklı kimlikle test edildi) Ortak driver
kuveytturk Kuveyt Türk BOA / TDV2.0 Taşındı (protokol seviyesinde — bkz. Durum) Kısmen (yalnızca ödeme)
vakif-katilim Vakıf Katılım BOA Taşındı (protokol seviyesinde — bkz. Durum) Test erişimi bekleniyor

Ödeme kuruluşları

Driver Kuruluş Node taşıması PHP'de doğrulama
iyzico iyzico Taşındı (protokol seviyesinde — bkz. Durum) Uçtan uca
tosla Tosla (AkÖde) Taşındı (protokol seviyesinde — bkz. Durum) Uçtan uca
akbank-pos Akbank (yeni JSON API) Taşındı (protokol seviyesinde — bkz. Durum) Uçtan uca
moka Moka United Taşındı (protokol seviyesinde — bkz. Durum) Uçtan uca
paytr PayTR Taşındı (protokol seviyesinde — bkz. Durum) Test erişimi bekleniyor
param Param ⚠️ TMSF kayyımlığında, aktif sürdürülmüyor
craftgate Craftgate Taşındı (protokol seviyesinde — bkz. Durum) Test vektörü
paratika Paratika (Payten) Taşındı (protokol seviyesinde — bkz. Durum) Dokümana göre
paycell Paycell (Turkcell) Taşındı (protokol seviyesinde — bkz. Durum) Kısmen

Yol haritası

  1. Her taşınan sürücüyü ilgili banka/sağlayıcının gerçek test ortamına karşı yeniden doğrula (API kimlikleri gerekiyor) — PHP tarafında Akbank/İşbank/QNB'de gördüğümüz gibi, kod doğru görünse de banka tarafında sürpriz çıkabilir; sonuçlara göre README/Durum tablosu kesinleştirilecek
  2. Express/Next.js için ince adapter paketleri
  3. npm'e yayınla

Geliştirme

npm install
npm run typecheck
npm test
npm run build

Lisans

MIT

About

Türk banka ve ödeme sağlayıcıları için tek arayüzlü, framework'e bağımlı olmayan ödeme kütüphanesi (Node.js/TypeScript) — Garanti, NestPay/Asseco, PayFor, PayFlex, PosNet, BOA, iyzico, Tosla, Moka, PayTR, Craftgate, Paratika, Paycell

Topics

Resources

Stars

70 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages