Garanti BBVA Virtual POS
Laravel Paketi Dokümantasyonu

Bu paket, Laravel projelerinde Garanti BBVA GVP Sanal POS entegrasyonunu sağlar. XML tabanlı doğrudan ödemeler, 3D Secure, OOS (Ortak Ödeme Sayfası), GarantiPay, puan/bonus kullanımı, tekrarlı ödemeler ve 8 farklı sorgu tipi desteklenmektedir.

GVP Uyumluluk: Paketin tüm işlem tipleri, repo içindeki resmi Help/GVP örnekleriyle fixture/test seviyesinde karşılaştırılmıştır. Madde madde kanıt listesi için GVP Compatibility Checklist dosyasına bakın.
📦 Paket
developertugrul/garanti-pos
Laravel 9 / 10 / 11 / 12 / 13
PHP 8.0+
🔐 Güvenlik
SHA1 (Legacy GVP)
SHA512 (Güncel Portal)
3D callback hash doğrulaması
💳 Desteklenen Akışlar
Non-3D, 3D Pay, 3D Full/Half
OOS, 3D OOS, GarantiPay/CUSTOM_PAY
30+ işlem tipi

Kurulum

Composer ile paketi projenize ekleyin, ardından servis sağlayıcısını ve yapılandırma dosyasını yayınlayın.

composer require developertugrul/garanti-pos
php artisan vendor:publish --provider="Developertugrul\GarantiPos\GarantiPosServiceProvider"

Bu komut config/garanti-pos.php dosyasını oluşturur. Tüm ayarlar .env üzerinden yönetilir.

Facade Kullanımı

use Developertugrul\GarantiPos\Facades\GarantiPos;

// Artık her yerden statik olarak çağrılabilir:
$response = GarantiPos::pay($orderData, $cardData);

Dependency Injection

use Developertugrul\GarantiPos\Services\GarantiPosService;

class PaymentController extends Controller
{
    public function __construct(private GarantiPosService $pos) {}

    public function charge(Request $request): array
    {
        return $this->pos->pay(
            ['order_id' => $request->order_id, 'amount' => $request->amount],
            ['number' => $request->card_number, ...]
        );
    }
}

Yapılandırma

Garanti BBVA tarafından sağlanan terminal, üye işyeri ve şifre bilgilerini .env dosyasına girin.

# ===== Ortam =====
GARANTI_POS_MODE=TEST                  # TEST veya PROD

# ===== Terminal Bilgileri =====
GARANTI_POS_TERMINAL_ID=12345678       # 8 haneli terminal kimliği
GARANTI_POS_TERMINAL_USER_ID=DENEME   # Terminal kullanıcı adı
GARANTI_POS_MERCHANT_ID=7000679        # Üye işyeri numarası (MerchantID)
GARANTI_POS_STORE_KEY=3DSecureAnahtari # 3D Secure şifresi

# ===== Para Birimi =====
GARANTI_POS_CURRENCY=949               # 949=TRY, 840=USD, 978=EUR

# ===== PROVAUT — Standart ödeme, ön provizyon, sorgular =====
GARANTI_POS_PROV_USER_ID=PROVAUT
GARANTI_POS_PROV_PASSWORD=SifreniziGirin

# ===== PROVRFN — İptal ve iade işlemleri =====
GARANTI_POS_REFUND_USER_ID=PROVRFN
GARANTI_POS_REFUND_PASSWORD=          # Boş bırakılırsa PROV_PASSWORD kullanılır

# ===== PROVOOS — OOS / GarantiPay form akışları =====
GARANTI_POS_PROV_OOS_USER_ID=PROVOOS
GARANTI_POS_PROV_OOS_PASSWORD=        # Boş bırakılırsa PROV_PASSWORD kullanılır
GARANTI_POS_OOS_USER_ID=oosuser
GARANTI_POS_OOS_FORM_CREDENTIAL_ROLE=oos   # 'oos' veya 'aut' (güncel portal için)

# ===== API Ayarları =====
GARANTI_POS_API_VERSION=v0.01          # 'v0.01' (SHA1) veya '512' (SHA512)
GARANTI_POS_CHANNEL_CODE=              # Kanal kodu (opsiyonel)
GARANTI_POS_HASH_ALGORITHM=sha1        # 'sha1' veya 'sha512'

# ===== Endpoint'ler =====
GARANTI_POS_TEST_ENDPOINT=https://sanalposprovtest.garanti.com.tr/VPServlet
GARANTI_POS_PROD_ENDPOINT=https://sanalposprov.garanti.com.tr/VPServlet
GARANTI_POS_TEST_3D_ENDPOINT=https://sanalposprovtest.garanti.com.tr/servlet/gt3dengine
GARANTI_POS_PROD_3D_ENDPOINT=https://sanalposprov.garanti.com.tr/servlet/gt3dengine

Kullanıcı Rol Tablosu

RolVarsayılan KullanıcıKapsam
AUTPROVAUTSatış, 3D Pay, 3D Model, preauth, postauth, tüm sorgular, recurringvoid
REFUNDPROVRFNcancel(), refund(), refundVoid(), postAuthVoid()
OOSPROVOOSOOS, 3D OOS, GarantiPay/CUSTOM_PAY form hash'leri
Şifre Önceliği: GARANTI_POS_REFUND_PASSWORD veya GARANTI_POS_PROV_OOS_PASSWORD boş bırakılırsa paket otomatik olarak GARANTI_POS_PROV_PASSWORD değerini kullanır. OOS/GarantiPay formlarında gönderilen terminalprovuserid hangi kullanıcıysa, hash hesabı o kullanıcının şifresiyle yapılır.

Dinamik Konfigürasyon

Çalışma zamanında belirli bir işlem için config'i geçici değiştirmek mümkündür:

GarantiPos::setConfig([
    'mode' => 'PROD',
    'terminal_id' => '99999999',
    'prov_password' => 'YeniSifre',
]);

Hash & Güvenlik

SecurityData Hesabı

Her GVP isteğinin HashData alanı, önce SecurityData türetilerek oluşturulur:

// SecurityData = SHA1(provPassword + 9 haneli terminalId)
// Örnek: terminal_id = "12345678" → "012345678"

use Developertugrul\GarantiPos\Services\HashGenerator;

$securityData = HashGenerator::generateSecurityData('ProvSifrem', '12345678');
// Çıktı: "D3A1F91A20C..." (40 karakter büyük harf hex)

XML Hash Hesabı (Non-3D)

AlgoritmaFormül
SHA1 (Legacy)orderId + terminalId + cardNumber + amount + securityData
SHA512 (Güncel)orderId + terminalId + cardNumber + amount + currencyCode + securityData
$hash = HashGenerator::generateHashData(
    orderId:      'SIPARIS-001',
    terminalId:   '12345678',
    cardNumber:   '5400111111111111',
    amount:       '10000',       // Kuruş cinsinden, nokta/virgül olmadan
    securityData: $securityData,
    algorithm:    'sha1',        // veya 'sha512'
    currencyCode: '949'          // SHA512 için gerekli
);

3D/OOS Form Hash Hesabı

AlgoritmaFormül
SHA1 (Legacy)terminalId + orderId + amount + successUrl + errorUrl + txntype + installment + storeKey + securityData
SHA512Aynı + currencyCode (amount'tan sonra)

Algoritma Seçimi

Hangi algoritmayı kullanmalıyım?
Repo içindeki resmi Help/GVP örnekleri SHA1 kullanır. Güncel Garanti BBVA geliştirici portali SHA512 ve apiversion=512 gerektirebilir. Terminaliniz hangi akışa kayıtlıysa o ayarı seçin.
SHA512 için: GARANTI_POS_HASH_ALGORITHM=sha512 ve GARANTI_POS_API_VERSION=512

GarantiPay Hash Notu

GarantiPay hosted form hash'i daima txntype=gpdatarequest değeriyle hesaplanır; satış tipi ise txnsubtype=sales olarak ayrı gönderilir. Paket bunu otomatik yönetir.

Doğrudan Ödeme (Non-3D XML)

Kart bilgilerini doğrudan bankaya gönderir; 3D Secure yönlendirmesi yapılmaz. PCI-DSS kapsamınız varsa veya MOTO işlemleri için uygundur.

pay(array $orderData, array $cardData, string $type = 'sales'): array PROVAUT

Standart satış işlemi. Taksitli satış için installment alanını doldurun.

Parametreler

AlanTürAçıklama
order_idstringSipariş kimliği (benzersiz olmalı)
amountstringTutar — kuruş cinsinden, nokta/virgülsüz ("10000" = 100,00 TL)
installmentstringTaksit sayısı — taksitsiz için boş string ""
currencystringPara birimi kodu (varsayılan: config'deki değer, örn. "949")
ip_addressstringMüşteri IP adresi
emailstringMüşteri e-posta
numberstringKart numarası
expire_monthstringSon kullanma ayı ("12")
expire_yearstringSon kullanma yılı — 2 hane ("28")
cvvstringCVV2/CVC2 kodu

Örnek — Peşin Satış

use Developertugrul\GarantiPos\Facades\GarantiPos;

$response = GarantiPos::pay(
    [
        'order_id'    => 'SIPARIS-' . uniqid(),
        'amount'      => '10000',      // 100,00 TL
        'installment' => '',           // Peşin
        'ip_address'  => request()->ip(),
        'email'       => 'musteri@ornek.com',
    ],
    [
        'number'       => '5400111111111111',
        'expire_month' => '12',
        'expire_year'  => '28',
        'cvv'          => '123',
    ]
);

if ($response['ProcReturnCode'] === '00') {
    // Ödeme başarılı
    $authCode   = $response['AuthCode'];
    $retrefNum  = $response['RetrefNum'];
}

Örnek — Taksitli Satış (3 Taksit)

$response = GarantiPos::pay(
    [
        'order_id'    => 'SIPARIS-TAK-001',
        'amount'      => '30000',   // 300,00 TL
        'installment' => '3',
    ],
    $cardData
);

Örnek Başarılı Yanıt

[
  'ResponseCode' => '00',
  'ResponseMessage' => 'Onaylandı',
  'ProcReturnCode' => '00',
  'OrderID' => 'SIPARIS-001',
  'RetrefNum' => '406310073566',
  'AuthCode' => '304919',
  'BatchNum' => '004063',
  'SequenceNum' => '000001',
  'HostRefNum' => '123456789012',
  'ProvDate' => '20260614 15:22:07',
]

Karşılık Gelen GVP XML — Type

<Transaction>
  <Type>sales</Type>
  <InstallmentCnt></InstallmentCnt>
  <Amount>10000</Amount>
  <CurrencyCode>949</CurrencyCode>
  <CardholderPresentCode>0</CardholderPresentCode>
</Transaction>

preAuth(array $orderData, array $cardData): array PROVAUT

Ön provizyon — kart limitini bloke eder ancak tahsilat yapmaz. Otel, araç kiralama gibi gecikmiş tahsilat senaryolarında kullanılır.

// Adım 1: Ön provizyon al
$preResponse = GarantiPos::preAuth(
    ['order_id' => 'PREAUTH-001', 'amount' => '50000'],
    ['number' => '5400111111111111', 'expire_month' => '12', 'expire_year' => '28', 'cvv' => '123']
);

$retrefNum = $preResponse['RetrefNum'];

postAuth(string $orderId, string $amount): array PROVAUT

Ön provizyon kapama — gerçek tahsilatı gerçekleştirir. Tutar, ön provizyon tutarından küçük veya eşit olabilir.

// Adım 2: Ön provizyonu kapat (örn. 3 gün sonra)
$postResponse = GarantiPos::postAuth(
    orderId: 'PREAUTH-001',
    amount:  '45000'    // Gerçek harcama — ön provizyon tutarından az olabilir
);

GVP XML Karşılıkları

<!-- preAuth --> <Type>preauth</Type>
<!-- postAuth --> <Type>postauth</Type>

cancel(string $orderId, string $originalRetrefNum = '', string $amount = '1'): array PROVRFN

Aynı gün içinde gerçekleşen bir işlemi iptal eder. Garanti GVP, iptal için Type=void kullanır (cancel değil).

$response = GarantiPos::cancel(
    orderId:           'SIPARIS-001',
    originalRetrefNum: '406310073566',   // preResponse'dan alınan RetrefNum
    amount:            '10000'
);

if ($response['ProcReturnCode'] === '00') {
    // İptal başarılı
}
GVP Notu: Garanti bankası API'sında iptal işlemi için XML'de Type=void gönderilir, cancel değil. Paket bunu otomatik halleder.

refund(string $orderId, string $amount, string $originalRetrefNum = ''): array PROVRFN

Önceki günlere ait bir işlemi iade eder. Kısmi iade desteklenir — orijinal tutardan az bir miktar gönderilebilir.

// Tam iade
$response = GarantiPos::refund(
    orderId:           'SIPARIS-001',
    amount:            '10000',
    originalRetrefNum: '406310073566'
);

// Kısmi iade (100 TL'lik işlemden 30 TL iade)
$response = GarantiPos::refund('SIPARIS-001', '3000', '406310073566');

if ($response['ProcReturnCode'] === '00') {
    // İade başarılı
}

postAuthVoid(string $orderId, string $originalRetrefNum = '', string $amount = '1'): array PROVRFN

Ön provizyon kapamanın iptali.

$response = GarantiPos::postAuthVoid('PREAUTH-001', $retrefNum);

refundVoid(string $orderId, string $originalRetrefNum = '', string $amount = '1'): array PROVRFN

Yapılmış bir iadenin iptali.

$response = GarantiPos::refundVoid('SIPARIS-001', $retrefNum);

3D Secure Ödeme

3D Secure ödeme akışı iki adımdan oluşur: (1) kullanıcıyı banka 3D sayfasına yönlendiren form oluşturma, (2) geri dönüşte hash/status kontrolü ve provizyonun alınması.

Güvenlik Seviyeleri: security_level alanına 3D_PAY, 3D_FULL veya 3D_HALF geçilebilir. 3D_PAY'de provizyon banka tarafından alınır (model A); 3D_FULL/3D_HALF'te siz alırsınız (model B — pay3DModel()).

build3DForm(array $orderData, array $cardData, string $successUrl, string $errorUrl, string $type = 'sales'): string

Kullanıcıyı bankanın 3D kimlik doğrulama sayfasına yönlendiren, otomatik gönderimli HTML form döner. Bu formu echo ile sayfaya basın; JavaScript otomatik submit yapar.

// routes/web.php
Route::get('/odeme/baslat', [PaymentController::class, 'start3D']);
Route::post('/odeme/basarili', [PaymentController::class, 'success3D'])->name('payment.success');
Route::post('/odeme/hata', [PaymentController::class, 'error3D'])->name('payment.error');

// app/Http/Controllers/PaymentController.php
public function start3D(Request $request): string
{
    return GarantiPos::build3DForm(
        [
            'order_id'       => 'ORDER-' . uniqid(),
            'amount'         => '15000',        // 150,00 TL
            'installment'    => '',
            'security_level' => '3D_PAY',       // 3D_PAY | 3D_FULL | 3D_HALF
            'ip_address'     => $request->ip(),
            'email'          => 'musteri@ornek.com',
        ],
        [
            'number'       => '5400111111111111',
            'expire_month' => '12',
            'expire_year'  => '28',
            'cvv'          => '123',
        ],
        route('payment.success'),   // Başarı URL'i (POST ile geri döner)
        route('payment.error')      // Hata URL'i
    );
}
Dönen string içinde <form action="https://...gt3dengine..."> ve gizli input'lar bulunur. Sayfa yüklenince JavaScript otomatik submit yapar. Sunucu tarafında bu string'i doğrudan döndürmek yeterlidir.

parse3DResponse(array $postData, array $expected = []): array

Garanti'nin geri POST ettiği callback verilerini doğrular. Hash, MD status, işlem kodu, sipariş kimliği ve tutarı ayrı ayrı raporlar.

public function success3D(Request $request): Response
{
    $check = GarantiPos::parse3DResponse(
        $request->all(),
        [
            'order_id' => session('order_id'),
            'amount'   => session('order_amount'),  // Kuruş cinsinden
        ]
    );

    // Tüm kontroller
    if (! $check['hash_valid']) {
        abort(400, 'Hash doğrulama başarısız');
    }
    if (! $check['md_status_accepted']) {
        abort(400, '3D kimlik doğrulama reddedildi: mdstatus=' . $check['md_status']);
    }
    if (! $check['order_matches']) {
        abort(400, 'Sipariş uyuşmazlığı');
    }

    // Buraya kadar her şey tamam — provizyona geçin
    return $this->finalizePayment($request->all());
}

Dönüş Değerleri

[
  'hash_valid' => true,
  'hash_source' => 'hashparams', // 'hashparams' | 'secure3dhash' | 'missing'
  'approved' => true,
  'procreturncode' => '00',
  'md_status' => '1', // 1-4 kabul, 5-9 ret
  'md_status_accepted' => true,
  'order_id' => 'ORDER-001',
  'amount' => '15000',
  'txntype' => 'sales',
  'order_matches' => true,
  'amount_matches' => true,
  'errors' => [],
]
MD Status Değerleri: 1=Tam doğrulama, 2=Kart 3D kayıtlı değil, 3=Banka 3D sistemi yok, 4=Doğrulama denenemedi. Değerler 1-4 arasında kabul edilir. 0, 5-9 reddedilmelidir.

pay3DModel(array $orderData, array $cardData, array $threeDResponse): array

3D_FULL veya 3D_HALF güvenlik seviyesinde, banka callback'inden sonra provizyonu siz alırsınız. Bu metod callback verilerini kullanarak bankaya ikinci XML isteği gönderir.

// parse3DResponse kontrollerinden geçtikten sonra:
$response = GarantiPos::pay3DModel(
    [
        'order_id' => session('order_id'),
        'amount'   => session('order_amount'),
    ],
    [],          // Kart bilgisi gerekmez — bankadan gelen mddata kullanılır
    $request->all()  // Tüm 3D callback POST verisi
);

if ($response['ProcReturnCode'] === '00') {
    // Provizyon başarılı
    $authCode = $response['AuthCode'];
}
pay3DModel() çağrısından önce parse3DResponse() ile md_status kontrolü yapılmalıdır. MD status geçerli olmadan provizyon çağrısı yapılırsa GarantiPosException fırlatılır.

OOS / Ortak Ödeme Sayfası

Kart bilgileri sitenizden alınmaz; müşteri bankanın kendi ödeme sayfasına yönlendirilir. PCI-DSS yükünü azaltır.

buildOOSForm(array $orderData, string $successUrl, string $errorUrl, string $type = 'sales'): string PROVOOS

$formHtml = GarantiPos::buildOOSForm(
    [
        'order_id'       => 'OOS-001',
        'amount'         => '10000',
        'security_level' => 'OOS_PAY',
        'ip_address'     => request()->ip(),
    ],
    route('payment.success'),
    route('payment.error')
);

echo $formHtml;

Ticari Kart OOS

use Developertugrul\GarantiPos\Enums\TransactionType;

$formHtml = GarantiPos::buildOOSForm(
    ['order_id' => 'COMMERCIAL-001', 'amount' => '50000'],
    route('payment.success'),
    route('payment.error'),
    TransactionType::COMMERCIAL_CARD   // txntype=commercialcard
);

build3DOOSForm(array $orderData, string $successUrl, string $errorUrl, string $type = 'sales'): string PROVOOS

3D doğrulama ile birleştirilmiş OOS akışı.

$formHtml = GarantiPos::build3DOOSForm(
    [
        'order_id'       => '3DOOS-001',
        'amount'         => '20000',
        'security_level' => '3D_OOS_PAY',    // 3D_OOS_PAY | 3D_OOS_FULL | 3D_OOS_HALF
        'installment'    => '',
    ],
    route('payment.success'),
    route('payment.error')
);
OOS formlarında hash hesabı için terminalprovuserid alanında gönderilen kullanıcı adının şifresi kullanılır. GARANTI_POS_OOS_FORM_CREDENTIAL_ROLE=oos (legacy) veya aut (güncel portal) olarak ayarlayın.

GarantiPay

GarantiPay, müşterilerin Garanti BBVA hesaplarıyla ödeme yapmasını sağlayan bir ödeme yöntemidir. İki akış desteklenmektedir: hosted form ve XML DataRequest.

buildGarantiPayForm(array $orderData, string $successUrl, string $errorUrl): string PROVOOS

GarantiPay hosted formunu oluşturur. Güvenlik seviyesi CUSTOM_PAY olarak gönderilir. Hash txntype=gpdatarequest ile hesaplanır.

$formHtml = GarantiPos::buildGarantiPayForm(
    [
        'order_id'    => 'GP-' . uniqid(),
        'amount'      => '25000',         // 250,00 TL
        'company_name' => 'Örnek Mağaza',
        'bnsuseflag'  => 'Y',             // Bonus kullanım izni
        'installments' => [
            ['number' => '1', 'amount' => '25000'],
            ['number' => '2', 'amount' => '25000'],
            ['number' => '3', 'amount' => '25000'],
        ],
        'items' => [
            [
                'number'       => '1',
                'product_id'   => 'SKU-001',
                'quantity'     => '1',
                'price'        => '25000',
                'total_amount' => '25000',
                'name'         => 'Ürün Adı',
            ],
        ],
    ],
    route('payment.garantipay.success'),
    route('payment.garantipay.error')
);

echo $formHtml;

garantiPayDataRequest(array $orderData): array PROVAUT

GarantiPay XML DataRequest — bankanın ödeme sayfasını açmak için gerekli bağlantıyı döner. Type=gpdatarequest, SubType=sales ile çalışır.

$response = GarantiPos::garantiPayDataRequest([
    'order_id'              => 'GP-DATA-001',
    'amount'                => '25000',
    'company_name'          => 'Örnek Mağaza',
    'return_server_url'     => route('payment.garantipay.server'),
    'return_url'            => route('payment.garantipay.return'),
    'gsm_number'            => '5XXXXXXXXX',
    'total_installment_count' => '3',
    'items' => [
        ['number' => '1', 'product_id' => 'SKU-001', 'quantity' => '1', 'price' => '25000'],
    ],
]);

// Yanıt içindeki GarantiPay bağlantısını müşteriye gönderin
$garantiPayUrl = $response['GarantiPaY']['Url'] ?? null;
GarantiPay 2.0 REST /api/garantipay/v0/init akışı bu paket kapsamı dışındadır. Bu paket, GVP dokümanındaki hosted form ve XML DataRequest akışını destekler.

Puan & Bonus

pointInquiry(array $cardData): array PROVAUT

Kartın mevcut bonus/puan bakiyesini sorgular. Type=rewardinq ile çalışır.

$response = GarantiPos::pointInquiry([
    'order_id'     => 'PUAN-SORGU-' . uniqid(),
    'number'       => '4282209004348015',
    'expire_month' => '05',
    'expire_year'  => '28',
    'cvv'          => '123',
]);

// Yanıtta puan bilgileri:
$bonusAmount = $response['RewardInqResult']['ChequeInfo'] ?? null;

rewardUsage(array $orderData, array $cardData, string $pointAmount): array PROVAUT

Ödeme sırasında bonus/puan kullanımı. Type=sales + RewardList/Reward bloğu ile çalışır.

$response = GarantiPos::rewardUsage(
    [
        'order_id'    => 'BONUS-001',
        'amount'      => '20000',        // 200,00 TL
        'reward_type' => 'BNS',          // BNS=Bonus, CHQ=Çek, PTS=Puan
    ],
    [
        'number'       => '4282209004348015',
        'expire_month' => '05',
        'expire_year'  => '28',
        'cvv'          => '123',
    ],
    '5000'   // Kullanılacak puan (50,00 TL değerinde)
);

// Kalan tutar kart ile tahsil edilir: 200 - 50 = 150 TL
Firma Bonus (FBB): Birden fazla reward satırı göndermek için pay() metodunda reward_list dizisi kullanın:
$response = GarantiPos::pay(
    [
        'order_id'    => 'FBB-001',
        'amount'      => '50000',
        'reward_list' => [
            ['type' => 'BNS', 'used_amount' => '5000', 'gained_amount' => '0'],
            ['type' => 'CHQ', 'used_amount' => '2000', 'gained_amount' => '0'],
        ],
    ],
    $cardData
);

DCC — Dinamik Kur Dönüşümü

Yabancı uyruklu kartlara kendi para biriminde ödeme imkânı sunar.

dccInquiry(string $orderId, string $cardNumber, string $amount): array PROVAUT

Kartın yabancı para birimi kurunu sorgular. Type=dccinq ile çalışır.

$dccInfo = GarantiPos::dccInquiry(
    orderId:    'DCC-SORGU-001',
    cardNumber: '4111111111111111',   // Yabancı kart numarası
    amount:     '10000'              // TL cinsinden tutar
);

// Yanıtta kur ve yabancı para tutarı gelir
$foreignAmount   = $dccInfo['DccInfo']['Amount'] ?? null;
$foreignCurrency = $dccInfo['DccInfo']['CurrencyCode'] ?? null;

payDcc(array $orderData, array $cardData): array PROVAUT

DCC ödemesi gerçekleştirir. SubType=dcc ve DCC/Currency bloğu ile çalışır.

$response = GarantiPos::payDcc(
    [
        'order_id'     => 'DCC-001',
        'amount'       => '10000',       // TL tutarı
        'dcc_currency' => '840',         // Yabancı para birimi kodu (840=USD)
    ],
    [
        'number'       => '4111111111111111',
        'expire_month' => '12',
        'expire_year'  => '28',
        'cvv'          => '456',
    ]
);

SMS Doğrulama

İki adımlı ödeme akışı: önce SMS gönderilir, ardından müşterinin girdiği kod ile işlem tamamlanır.

Adım 1: paySms(array $orderData, array $cardData): array

SubType=sms ile preauth/sales isteği gönderir; banka müşteriye SMS atar.

// Adım 1: SMS gönder
$response = GarantiPos::paySms(
    ['order_id' => 'SMS-001', 'amount' => '10000'],
    ['number' => '5400111111111111', 'expire_month' => '12', 'expire_year' => '28', 'cvv' => '123']
);

Adım 2: smsPostAuth(array $orderData, string $smsPassword): array

Müşterinin girdiği SMS kodunu bankaya doğrulatır ve provizyonu tamamlar.

// Adım 2: Müşteri SMS kodunu girdi → tamamla
$response = GarantiPos::smsPostAuth(
    ['order_id' => 'SMS-001', 'amount' => '10000'],
    '123456'   // Müşterinin girdiği SMS şifresi
);

if ($response['ProcReturnCode'] === '00') {
    // SMS doğrulama + ödeme başarılı
}

Ekstre Doğrulama

Kart sahibinin kimliğini hesap ekstresi bilgisiyle doğrular. SubType=extre + Verification/ExtreInfo bloğu kullanılır.

payExtre(array $orderData, array $cardData, string $extreInfo): array

// Ekstre doğrulama ile satış
$response = GarantiPos::payExtre(
    ['order_id' => 'EXTRE-001', 'amount' => '10000'],
    $cardData,
    '123456789'   // Ekstre/hesap bilgisi
);

preAuthExtre(array $orderData, array $cardData, string $extreInfo): array

// Ekstre doğrulama ile ön provizyon
$response = GarantiPos::preAuthExtre(
    ['order_id' => 'EXTRE-002', 'amount' => '10000'],
    $cardData,
    '123456789'
);

postAuthExtre(array $orderData, string $extreInfo, array $cardData = []): array

// Ekstre doğrulama ile ön provizyon kapama
$response = GarantiPos::postAuthExtre(
    ['order_id' => 'EXTRE-002', 'amount' => '10000'],
    '123456789'
);

TCKN / Kimlik Doğrulama

Kart sahibinin TC Kimlik Numarası ile kimlik doğrulaması yapar. Type=identifyinq ve Verification/Identity bloğu kullanılır.

identifyInquiry(array $orderData, array $cardData, string $tckn): array PROVAUT

$response = GarantiPos::identifyInquiry(
    ['order_id' => 'TCKN-001', 'amount' => '100'],
    [
        'number'       => '5400111111111111',
        'expire_month' => '12',
        'expire_year'  => '28',
        'cvv'          => '123',
    ],
    '12345678901'   // TC Kimlik Numarası
);

$matched = $response['ProcReturnCode'] === '00';

CepBank

Garanti BBVA'nın mobil ödeme çözümü. Müşterinin GSM numarası ile ödeme yapmasını sağlar.

payCepBank(array $orderData, array $cepBankData): array PROVAUT

CepBank/GSMNumber/PaymentType/HashDate/HashValue XML bloğu ile çalışır.

$response = GarantiPos::payCepBank(
    [
        'order_id' => 'CEP-001',
        'amount'   => '10000',
    ],
    [
        'gsm_number'   => '5XXXXXXXXX',
        'payment_type' => 'K',                   // K=Kredi kartı bakiye
        'hash_date'    => date('Ymd'),           // YYYYMMDD
        'hash_value'   => 'BANKA_TARAFINDAN_URETILMIS_HASH',
    ]
);

Fatura & Özel Ödemeler

payUtility(array $orderData, array $cardData, array $utilityPaymentData): array PROVAUT

Su, elektrik, doğalgaz gibi fatura ödemeleri. UtilityPayment XML bloğu kullanılır.

$response = GarantiPos::payUtility(
    ['order_id' => 'FATDIR-001', 'amount' => '25000'],
    $cardData,
    [
        'invoice_id'  => 'FAT-2026-001',
        'invoice_type' => '1',   // 1=Elektrik, 2=Su, 3=Doğalgaz
        'subscriber_no' => '1234567890',
    ]
);

payGsmUnitSales(array $orderData, array $cardData, array $gsmUnitSalesData): array PROVAUT

GSM kontör/paket satışı. GSMUnitSales XML bloğu kullanılır.

$response = GarantiPos::payGsmUnitSales(
    ['order_id' => 'GSM-001', 'amount' => '5000'],
    $cardData,
    [
        'gsm_quantity'    => '2',
        'operator_code'   => 'TURKCELL',
        'subscriber_msisdn' => '5XXXXXXXXX',
    ]
);

payMoneyCard(array $orderData, array $cardData, array $moneyCardData): array PROVAUT

MoneyCard / hediye kartı ödemesi. MoneyCard XML bloğu kullanılır.

$response = GarantiPos::payMoneyCard(
    ['order_id' => 'MONEY-001', 'amount' => '10000'],
    $cardData,
    [
        'money_invoice'   => '10000',
        'money_card_no'   => 'HEDIYE-KART-12345',
    ]
);

Tekrarlı Ödemeler (Recurring)

Abonelik, taksit veya periyodik tahsilat için kullanılır. Order/Recurring XML bloğu ile çalışır.

payRecurring(array $orderData, array $cardData, array $recurringData): array PROVAUT

Sabit Tutarlı Tekrarlı Satış

$response = GarantiPos::payRecurring(
    [
        'order_id' => 'REC-001',
        'amount'   => '9900',   // Her ay çekilecek tutar (99,00 TL)
    ],
    $cardData,
    [
        'type'               => 'R',         // R=Tekrarlı
        'total_payment_num'  => '12',        // Toplam taksit sayısı
        'frequency_type'     => 'MONTHLY',   // MONTHLY | WEEKLY | DAILY
        'frequency_interval' => '1',         // Her 1 ayda bir
        'start_date'         => '20260701',  // İlk çekim tarihi YYYYMMDD
    ]
);

Değişken Tutarlı Tekrarlı Satış

$response = GarantiPos::payRecurring(
    ['order_id' => 'REC-VAR-001', 'amount' => '1000'],
    $cardData,
    [
        'type'               => 'G',         // G=Değişken
        'total_payment_num'  => '3',
        'frequency_type'     => 'MONTHLY',
        'frequency_interval' => '1',
        'start_date'         => '20260701',
    ]
);

recurringUpdate(string $orderId, array $paymentList): array PROVAUT

Bekleyen tekrarlı taksit tutarlarını günceller. Type=recurringupdate kullanılır.

// Bekleyen 2 taksitin tutarını güncelle
$response = GarantiPos::recurringUpdate(
    'REC-VAR-001',
    [
        ['Amount' => '12000'],   // 1. taksit: 120 TL
        ['Amount' => '15000'],   // 2. taksit: 150 TL
    ]
);

recurringCancel(string $orderId, string $amount = '100'): array PROVAUT

Bekleyen tüm tekrarlı taksitleri iptal eder. Type=recurringvoid, PROVAUT (PROVRFN değil) kullanılır.

$response = GarantiPos::recurringCancel('REC-001');
Dikkat: recurringCancel() standart cancel()'dan farklı olarak PROVAUT kullanır. Bu, resmi GVP dökümanındaki davranışla örtüşür.

Kredili & Vadeli Satışlar

payExtendedCredit(array $orderData, array $cardData): array PROVAUT

Tüketici kredisi / vadeli taksit. Type=extendedcredit ile çalışır.

$response = GarantiPos::payExtendedCredit(
    [
        'order_id'    => 'KREDIT-001',
        'amount'      => '100000',    // 1.000,00 TL
        'installment' => '12',
    ],
    $cardData
);

payDownPaymentSale(array $orderData, array $cardData): array PROVAUT

Peşinatlı taksitli satış. Type=sales + DownPaymentRate alanı ile çalışır.

$response = GarantiPos::payDownPaymentSale(
    [
        'order_id'          => 'PESINAT-001',
        'amount'            => '50000',
        'installment'       => '6',
        'down_payment_rate' => '20',    // %20 peşinat
    ],
    $cardData
);

payDelayedSale(array $orderData, array $cardData): array PROVAUT

Otelemeli satış — tahsilat belirli gün sonra yapılır. Type=sales + DelayDayCount ile çalışır.

$response = GarantiPos::payDelayedSale(
    [
        'order_id'       => 'OTELEME-001',
        'amount'         => '30000',
        'delay_day_count' => '7',     // 7 gün sonra tahsil et
    ],
    $cardData
);

Ticari Kart Vadeli İşlem

payCommercialCardExtendedCredit(array $orderData, array $cardData): array PROVAUT

Ticari kart ile vadeli işlem. Type=commercialcardextendedcredit ve CommercialCardExtendedCredit/PaymentList XML bloğu kullanılır.

$response = GarantiPos::payCommercialCardExtendedCredit(
    [
        'order_id' => 'TIC-001',
        'amount'   => '60000',
        'payments' => [
            ['number' => '1', 'amount' => '20000'],  // 1. ödeme: 200 TL
            ['number' => '2', 'amount' => '20000'],  // 2. ödeme: 200 TL
            ['number' => '3', 'amount' => '20000'],  // 3. ödeme: 200 TL
        ],
    ],
    $cardData
);

Sorgular

Tüm sorgu metodları PROVAUT kimlik bilgileriyle çalışır.

orderInquiry(string $orderId, string $amount = '100'): array

Belirli bir siparişin son durumunu sorgular. Type=orderinq

$response = GarantiPos::orderInquiry('SIPARIS-001');

$status    = $response['ProcReturnCode'];
$authCode  = $response['AuthCode'] ?? null;
$retrefNum = $response['RetrefNum'] ?? null;

Örnek Yanıt

[
  'ProcReturnCode' => '00',
  'OrderID' => 'SIPARIS-001',
  'AuthCode' => '304919',
  'RetrefNum' => '406310073566',
  'TransactionType' => 'sales',
  'Amount' => '10000',
]

orderHistoryInquiry(string $orderId, string $amount = '100'): array

Bir siparişin tüm işlem geçmişini listeler (iade, iptal vb.). Type=orderhistoryinq

$response = GarantiPos::orderHistoryInquiry('SIPARIS-001');

orderListInquiry(?string $startDate, ?string $endDate, int $listPageNum = 1, string $orderId = '', string $amount = '100'): array

Tarih aralığına göre sipariş listesi sorgular. Type=orderlistinqStartDate/EndDate Order altında, ListPageNum Transaction altında gönderilir.

// Belirli tarih aralığı
$response = GarantiPos::orderListInquiry(
    startDate:   '01/06/2026',   // GG/AA/YYYY formatı
    endDate:     '14/06/2026',
    listPageNum: 1
);

// İkinci sayfa
$response = GarantiPos::orderListInquiry('01/06/2026', '14/06/2026', 2);

batchInquiry(?string $batchNum = null, int $listPageNum = 1, string $orderId = '', string $amount = '100'): array

Günsonu (batch) sorgulama. Type=batchinqBatchNum ve ListPageNum Transaction altında.

// Mevcut batch (bugünkü)
$response = GarantiPos::batchInquiry();

// Belirli batch numarası
$response = GarantiPos::batchInquiry('004063');

// Sayfalı listeleme
$response = GarantiPos::batchInquiry(null, 2);

binInquiry(string $binNumber = '', string $amount = '100'): array

Kart BIN (ilk 6-8 hane) bilgisini sorgular; kart tipi, banka, yurt içi/dışı bilgisi döner. Type=bininq

$response = GarantiPos::binInquiry('540011');

$cardType   = $response['CardInfo']['CardType'] ?? null;     // 'KREDI' / 'DEBIT'
$bankName   = $response['CardInfo']['BankName'] ?? null;
$issuerId   = $response['CardInfo']['IssuerID'] ?? null;

campaignCodeInquiry(string $campaignCode, string $amount = '100'): array

Kampanya kodu ile kampanya detaylarını sorgular. Type=campaigncodeinq

Yazım Notu: GVP resmi API'sinde alan adı CampaingCode şeklindedir (typo — tek 'i'). Paket resmi yazımı aynen korur.
$response = GarantiPos::campaignCodeInquiry('KAMPANYA2026');

extendedCreditInquiry(string $orderId, string $amount = '100'): array

Tüketici kredisi / vadeli taksit sorgusu. Type=extendedcreditinq

$response = GarantiPos::extendedCreditInquiry('KREDIT-001');

settlementInquiry(string $date, array $transactionSummaries = [], array $options = []): array

Mutabakat sorgusu. Root SettlementInq bloğu ile çalışır (diğer sorgulardan farklı yapı).

$response = GarantiPos::settlementInquiry(
    date:                '20260614',  // YYYYMMDD
    transactionSummaries: [
        ['Type' => 'sales',  'Count' => '5', 'Amount' => '50000', 'CurrencyCode' => '949'],
        ['Type' => 'refund', 'Count' => '1', 'Amount' => '10000', 'CurrencyCode' => '949'],
    ]
);

Form Ek Alanları

3D / OOS formlarına ek veri göndermek için orderData dizisine aşağıdaki alanlar eklenebilir. Tüm alan isimleri hem snake_case hem de orijinal GVP adıyla kabul edilir.

Ürün Listesi (ItemList)

GarantiPos::build3DForm(
    [
        'order_id' => 'ORDER-001',
        'amount'   => '15000',
        'items'    => [
            [
                'number'       => '1',
                'product_code' => 'SKU-001',
                'quantity'     => '1',
                'price'        => '10000',
                'total_amount' => '10000',
                'name'         => 'Ürün A',
                'description'  => 'Ürün açıklaması',
            ],
            [
                'number'       => '2',
                'product_code' => 'SKU-002',
                'quantity'     => '2',
                'price'        => '2500',
                'total_amount' => '5000',
                'name'         => 'Ürün B',
            ],
        ],
    ],
    $cardData, $successUrl, $errorUrl
);

Adres Listesi (AddressList)

'addresses' => [
    [
        'type'        => 'B',          // B=Fatura, S=Teslimat
        'name'        => 'Ahmet',
        'lastname'    => 'Yılmaz',
        'company'     => 'Firma A.Ş.',
        'address'     => 'Atatürk Cad. No:1',
        'district'    => 'Şişli',
        'city'        => 'Istanbul',
        'postal_code' => '34000',
        'country'     => 'TR',
        'phone_number' => '02121234567',
        'gsm_number'  => '5XXXXXXXXX',  // GVP: GsmNumber (büyük G, küçük sm)
    ],
    [
        'type'    => 'S',   // Teslimat adresi
        'name'    => 'Ahmet',
        'address' => 'Farklı Teslimat Adresi No:5',
        'city'    => 'Ankara',
    ],
],
GSM numarası için resmi GVP alan adı GsmNumber'dır (büyük G, küçük sm). Paket bu yazımı korur.

Yorum Listesi (CommentList)

'comments' => [
    ['number' => '1', 'text' => 'Özel sipariş notu'],
    ['number' => '2', 'text' => 'Kargo talimatı'],
],

Bonus/Puan Kullanımı (RewardList)

'reward_list' => [
    ['type' => 'BNS', 'used_amount' => '5000', 'gained_amount' => '0'],
    ['type' => 'CHQ', 'used_amount' => '2000', 'gained_amount' => '0'],
],

Çek Listesi (ChequeList)

'cheque_list' => [
    ['type' => 'P', 'amount' => '5000', 'count' => '1'],
],

Özel Form Alanları (Passthrough)

GVP'nin desteklediği ancak paket tarafından özel olarak işlenmeyen alanları doğrudan form'a eklemek için:

'form_fields' => [
    'customfield1' => 'deger1',
    'customfield2' => 'deger2',
],

Tam Alan Adı Tablosu

Paket Alanı (snake_case)GVP XML / Form AdıAçıklama
company_namecompanynameFirma adı
group_idordergroupidSipariş grup ID
down_payment_ratetxndownpayratePeşinat oranı (%)
delay_day_counttxndelaydaycntÖtelem gün sayısı
moto_indtxnmotoindMOTO göstergesi
utility_pay_invoice_idutilitypayinvoiceidFatura no
gsm_quantitygsmquantityGSM kontör adedi
money_invoicemoneyinvoiceMoneyCard fatura
security_levelsecure3dsecuritylevel3D güvenlik seviyesi
installmenttxninstallmentcountTaksit sayısı
ip_addresscustomeripaddressMüşteri IP
emailcustomeremailaddressMüşteri e-posta

Enum Referansı

Currency (Para Birimi)

use Developertugrul\GarantiPos\Enums\Currency;

Currency::TRY  // '949' — Türk Lirası
Currency::USD  // '840' — Amerikan Doları
Currency::EUR  // '978' — Euro
Currency::GBP  // '826' — İngiliz Sterlini
Currency::JPY  // '392' — Japon Yeni
Currency::RUB  // '643' — Rus Rublesi

// Kullanım örneği
GarantiPos::pay(
    ['order_id' => 'USD-001', 'amount' => '10000', 'currency' => Currency::USD],
    $cardData
);

TransactionType (İşlem Tipi)

SabitDeğerAçıklama
SALE'sales'Standart satış
CANCEL / VOID'void'İptal
REFUND'refund'İade
PRE_AUTH'preauth'Ön provizyon
POST_AUTH'postauth'Ön provizyon kapama
REWARD_INQUIRY'rewardinq'Puan sorgu
ORDER_INQUIRY'orderinq'Sipariş sorgu
ORDER_HISTORY_INQUIRY'orderhistoryinq'Sipariş geçmişi sorgusu
ORDER_LIST_INQUIRY'orderlistinq'Sipariş listesi sorgusu
BATCH_INQUIRY'batchinq'Günsonu sorgusu
BIN_INQUIRY'bininq'BIN sorgusu
DCC_INQUIRY'dccinq'DCC kur sorgusu
CAMPAIGN_CODE_INQUIRY'campaigncodeinq'Kampanya sorgusu
RECURRING_VOID'recurringvoid'Tekrarlı iptal
RECURRING_UPDATE'recurringupdate'Tekrarlı güncelleme
IDENTIFY_INQUIRY'identifyinq'TCKN sorgusu
EXTENDED_CREDIT'extendedcredit'Tüketici kredisi
COMMERCIAL_CARD'commercialcard'Ticari kart (OOS form)
COMMERCIAL_CARD_EXTENDED_CREDIT'commercialcardextendedcredit'Ticari kart vadeli
CEPBANK'cepbank'CepBank ödemesi
GARANTI_PAY_DATA_REQUEST'gpdatarequest'GarantiPay DataRequest

Hata Yönetimi

GarantiPosException

Paket, geçersiz parametre, cURL hatası veya XML ayrıştırma hatası durumunda GarantiPosException fırlatır:

use Developertugrul\GarantiPos\Exceptions\GarantiPosException;

try {
    $response = GarantiPos::pay($orderData, $cardData);
} catch (GarantiPosException $e) {
    Log::error('GarantiPos hatası: ' . $e->getMessage());
    // Kullanıcıya hata göster
}

ProcReturnCode Değerleri

KodAnlam
00Başarılı
01Kart sahibini arayın
05İşlem onaylanmadı (genel red)
12Geçersiz işlem
51Yetersiz bakiye / limit
54Kartın son kullanma tarihi geçmiş
57Kart sahibine izin verilmemiş işlem
62Kısıtlı kart
91Banka kartı veren sisteminde arıza
96Sistem arızası

Yanıt Kontrol Örüntüsü

$response = GarantiPos::pay($orderData, $cardData);

$success = isset($response['ProcReturnCode']) && $response['ProcReturnCode'] === '00';

if ($success) {
    $order->update([
        'payment_status' => 'paid',
        'auth_code'      => $response['AuthCode'],
        'retref_num'     => $response['RetrefNum'],
        'batch_num'      => $response['BatchNum'],
        'sequence_num'   => $response['SequenceNum'],
        'host_ref_num'   => $response['HostRefNum'],
    ]);
} else {
    Log::warning('Ödeme reddedildi', [
        'order_id'      => $orderData['order_id'],
        'return_code'   => $response['ProcReturnCode'] ?? 'N/A',
        'message'       => $response['ResponseMessage'] ?? 'Bilinmiyor',
    ]);
}

Yanıt Yapısı

Tüm metodlar aşağıdaki temel alanları içeren bir dizi döner. İşlem tipine göre ek alanlar bulunabilir:

[
  'ResponseCode' => '00',
  'ResponseMessage' => 'Onaylandı',
  'TerminalProvUserID' => 'PROVAUT',
  'ProcReturnCode' => '00',
  'ProvDate' => '20260614 15:22:07',
  'IsEnrolled' => 'Y',
  'OrderID' => 'SIPARIS-001',
  'RetrefNum' => '406310073566',
  'BatchNum' => '004063',
  'SequenceNum' => '000001',
  'AuthCode' => '304919',
  'HostRefNum' => '123456789012',
]

Test

Birim Testleri Çalıştırma

composer install
vendor/bin/phpunit --configuration phpunit.xml.dist

WSL / XAMPP ortamında:

cmd.exe /c "cd /d C:\xampp\htdocs\libs\garanti-pos && C:\xampp\php\php.exe vendor\bin\phpunit --configuration phpunit.xml.dist"

Test Kapsamı

Test DosyasıKapsam
HashGeneratorTestSecurityData, SHA1/SHA512 XML hash, 3D form hash, callback hashparams doğrulaması, secure3dhash doğrulaması
XmlBuilderTestTekrarlayan liste düğümleri (AddressList/Address vb.), XML değer tek encode
GarantiPosServicePayloadTest16 senaryo: SHA512 mod, credential routing, özel XML blokları, form alanları, sorgu payload'ları, 3D callback işleme
Önemli: Testler bankaya canlı istek atmaz. Hash hesaplamaları, XML yapısı ve form alanları fixture/stub ile doğrulanır. Canlı banka testi gerçek merchant/terminal bilgileriyle ayrıca manuel smoke test olarak yapılmalıdır.

XML Payload İncelemesi (Canlı Banka İsteği Göndermeden)

// Test ortamında banka isteği göndermeden oluşturulan XML'i görmek için:
$service = app(\Developertugrul\GarantiPos\Services\GarantiPosService::class);

$xml = $service->buildRequestXml([
    'GVPSRequest' => [
        'Mode'    => 'TEST',
        'Version' => 'v0.01',
        // ...
    ],
]);

echo $xml;

Test Ortamı Bilgileri

Garanti BBVA test ortamı endpoint'i: https://sanalposprovtest.garanti.com.tr/VPServlet
3D test endpoint'i: https://sanalposprovtest.garanti.com.tr/servlet/gt3dengine
Test kart ve terminal bilgileri için Garanti BBVA entegrasyon desteğine başvurun.

Paket: developertugrul/garanti-pos — v1.0.17 — MIT Lisansı
Yazar: Tuğrul Yıldırım <contact@tugrulyildirim.com>
GVP Uyumluluk: GVP_COMPATIBILITY_CHECKLIST.md