done6666/paytr-sdk

Framework bağımsız PayTR ödeme altyapısı PHP SDK

Maintainers

Package info

github.com/done6666/paytr-sdk

pkg:composer/done6666/paytr-sdk

Statistics

Installs: 9

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v1.0.0 2026-01-26 22:00 UTC

This package is not auto-updated.

Last update: 2026-04-07 22:41:00 UTC


README

PayTR ödeme altyapısı için framework bağımsız PHP SDK. PSR-4, PSR-12, PHP 8.0+.

  • Iframe (Token + iframe URL)
  • Direkt API (Ödeme, BIN, Taksit Oranları)
  • Kart Saklama API (CAPI) (Yeni kart ekleme, kayıtlı karttan ödeme, kart listesi, kart silme, tekrarlayan ödeme)
  • Callback doğrulama (Iframe + Direkt API + CAPI ortak bildirim)
  • Durum sorgulama
  • İade

Kurulum

composer require done6666/paytr-sdk

HTTP istekleri için Guzzle adapter (opsiyonel):

composer require guzzlehttp/guzzle

Hızlı Başlangıç

Önerilen kurulum Options ile yapılır. (Options fluent builder’dır; ileri kullanımda Config de kullanılabilir.)

use Done\PayTR\Options;
use Done\PayTR\Client;
use Done\PayTR\Adapters\GuzzleHttpClient;

$options = (new Options())
    ->setMerchantId('MAĞAZA_NO')
    ->setMerchantKey('MAĞAZA_PAROLA')
    ->setMerchantSalt('MAĞAZA_GİZLİ_ANAHTAR')
    ->setTestMode(true);

$client = new Client($options, GuzzleHttpClient::default());

Kendi HTTP client’ınız için Done\PayTR\Contracts\HttpClient arayüzünü implement edebilirsiniz.

Önemli Notlar

1) user_ip (kritik)

Direkt API / Kart Saklama API çağrılarında user_ip alanı mutlaka gerçek müşteri IP’si olmalıdır. Örneğin reverse proxy arkasındaysanız doğru header’dan almayı unutmayın.

2) Idempotency (kritik)

PayTR bildirimleri (callback) aynı merchant_oid için birden fazla kez gelebilir. Siparişinizi idempotent işleyin:

  • Sipariş daha önce “paid/failed” durumuna alınmışsa tekrar işlemeyin.
  • Önce DB’de sipariş durumunu kontrol edin.

3) sync_mode farkı

  • sync_mode=0 (varsayılan): SDK payload üretir, siz HTML form ile PayTR’ye POST edersiniz.
  • sync_mode=1: SDK HTTP çağrısını kendi yapar (JSON yanıt alırsınız). Bunun için mağazada ilgili yetkilerin açık olması gerekir.

4) Kart verisi güvenliği

Ham kart numarası / CVV / tam kart bilgisini:

  • loglamayın,
  • gereksiz yere saklamayın,
  • HTTPS kullanın,
  • PCI-DSS yükümlülüklerinizi değerlendirin.

Desteklenen Entegrasyonlar

  • Iframe API

    • Token oluşturma (ödeme formu başlatma)
  • Direkt API

    • Ödeme isteği
    • BIN sorgulama
    • Taksit oranları
  • Kart Saklama API (CAPI)

    • Yeni kart ekleme (ödeme sırasında, store_card=1)
    • Kayıtlı karttan ödeme
    • Kayıtlı kart listesi (CAPI LIST)
    • Kayıtlı kart silme (CAPI DELETE)
    • Kayıtlı kartla tekrarlayan ödeme (recurring_payment=1)
  • Callback doğrulama

    • Iframe + Direkt API + CAPI aynı doğrulama ile çalışır
  • Durum sorgulama

  • İade

1) Iframe ile ödeme başlatma (token)

Buyer, Basket, CreateTokenRequest ile token alıp iframe URL’i üretin.

PayTR payment_amount alanını kuruş bazlı kullanır. SDK’da:

  • setAmountFromTL('34.56') → SDK 3456 olarak gönderir
  • direkt kuruş için setAmountKurus(3456)
use Done\PayTR\Model\Buyer;
use Done\PayTR\Model\Basket;
use Done\PayTR\Model\BasketItem;
use Done\PayTR\Request\Iframe\CreateTokenRequest;

$buyer = new Buyer(
    email: 'musteri@ornek.com',
    name: 'Ad Soyad',
    phone: '5551234567',
    ip: $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0',
    addressLine: 'Teslimat adresi'
);

$basket = (new Basket())
    ->addItem(new BasketItem('Ürün adı 1', '18.00', 1))
    ->addItem(new BasketItem('Ürün adı 2', '33.25', 2));

$request = (new CreateTokenRequest())
    ->setMerchantOid('SIPARIS-' . uniqid())
    ->setAmountFromTL('34.56')
    ->setCurrency('TL')
    ->setBuyer($buyer)
    ->setBasket($basket)
    ->setOkUrl('https://siteniz.com/odeme-basarili')
    ->setFailUrl('https://siteniz.com/odeme-hata')
    ->setInstallmentPolicy(noInstallment: false, maxInstallment: 12);

$response = $client->iframe()->createToken($request);
$iframeSrc = $client->iframe()->iframeUrl($response->token);

HTML iframe:

<script src="https://www.paytr.com/js/iframeResizer.min.js"></script>
<iframe src="<?= htmlspecialchars($iframeSrc) ?>" id="paytriframe" frameborder="0" scrolling="no" style="width: 100%;"></iframe>
<script>iFrameResize({}, '#paytriframe');</script>

Sipariş onay/iptal işlemleri bildirim URL (callback) üzerinden yapılmalıdır. merchant_ok_url / merchant_fail_url yalnızca müşteri yönlendirmesidir.

1b) Direkt API

Direkt API’de ödeme formu sizin sunucunuzdadır; kart bilgileri PayTR’ye gönderilir.

Akışlar

  • sync_mode=0 (varsayılan)

    • $request->toPayload($options) ile payload alırsınız.
    • Kendi HTML formunuzdan https://www.paytr.com/odeme adresine POST edersiniz.
    • Sonuç, PayTR’nin yönlendirdiği merchant_ok_url / merchant_fail_url sayfalarında görülür.
  • sync_mode=1

    • SDK createPayment() ile POST atar.
    • JSON yanıt döner: success | wait_callback | failed
    • Mağazada Non3D ve sync yetkisi gerekebilir.

Örnek — Direkt API ödeme (sync_mode=1)

use Done\PayTR\Model\Buyer;
use Done\PayTR\Model\Basket;
use Done\PayTR\Model\BasketItem;
use Done\PayTR\Model\Card;
use Done\PayTR\Request\Direct\CreatePaymentRequest;

$buyer = new Buyer(
    email: 'musteri@ornek.com',
    name: 'Ad Soyad',
    phone: '5551234567',
    ip: $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0',
    addressLine: 'Teslimat adresi'
);

$basket = (new Basket())
    ->addItem(new BasketItem('Ürün 1', '50.00', 1))
    ->addItem(new BasketItem('Ürün 2', '25.50', 2));

$card = new Card('KART SAHİBİ ADI', '4111111111111111', '12', '30', '000');

$request = (new CreatePaymentRequest())
    ->setMerchantOid('DIR-' . uniqid())
    ->setPaymentAmount('101.00')
    ->setBuyer($buyer)
    ->setBasket($basket)
    ->setCard($card)
    ->setOkUrl('https://siteniz.com/basarili')
    ->setFailUrl('https://siteniz.com/hata')
    ->setInstallmentCount(0)
    ->setNon3d(true)
    ->setSyncMode(true);

$response = $client->directApi()->createPayment($request);

if ($response->isSuccess()) {
    // Ödeme başarılı
} elseif ($response->isWaitCallback()) {
    // Sonuç callback üzerinden gelecek
}

BIN sorgulama ve taksit oranları

use Done\PayTR\Request\Direct\BinLookupRequest;
use Done\PayTR\Request\Direct\InstallmentRatesRequest;

$binRequest = (new BinLookupRequest())->setBinNumber('411111');
$binResult = $client->directApi()->binLookup($binRequest);
if ($binResult->isSuccess()) {
    $brand = $binResult->brand; // bonus, axess, vb.
}

$taksitRequest = (new InstallmentRatesRequest())->setRequestId('taksit-' . uniqid());
$taksitResult = $client->directApi()->installmentRates($taksitRequest);
if ($taksitResult->isSuccess()) {
    $oranlar = $taksitResult->oranlar;
    $maxTaksit = $taksitResult->maxInstNonBus;
}

sync_mode=0 için: $request->toPayload($options) alıp kendi formunuzla $client->directApi()->getPaymentFormUrl() (varsayılan https://www.paytr.com/odeme) adresine POST edin.

1c) Kart Saklama API (CAPI)

Kart Saklama ile kullanıcıların kartlarını PayTR’de saklayabilir; kayıtlı karttan ödeme alabilir, listeleyebilir, silebilir ve tekrarlayan ödeme yapabilirsiniz.

Erişim:

$client->cardStorage();

Endpoint / token özeti

  • CAPI LIST: POST /odeme/capi/list Token: utoken + merchant_salt
  • CAPI DELETE: POST /odeme/capi/delete Token: ctoken + utoken + merchant_salt
  • Yeni kart / kayıtlı karttan ödeme / tekrarlayan ödeme: POST /odeme Token formülü Direkt API ile aynıdır: merchant_id + user_ip + merchant_oid + email + payment_amount + payment_type + installment_count + currency + test_mode + non_3d + merchant_salt

Tekrarlayan ödemede recurring_payment alanı token string’ine dahil edilmez.

a) Yeni kart ekleme (ödeme sırasında) — store_card=1

İlk kartta utoken göndermeyin; PayTR callback üzerinden utoken/ctoken döner. Aynı kullanıcıya yeni kart eklerken mevcut utoken’ı gönderin.

use Done\PayTR\Request\CardStorage\AddCardRequest;

$request = (new AddCardRequest())
    ->setMerchantOid('SIP-' . uniqid())
    ->setPaymentAmount('50.00')
    ->setBuyer($buyer)
    ->setBasket($basket)
    ->setCard($card)
    ->setOkUrl('https://siteniz.com/ok')
    ->setFailUrl('https://siteniz.com/fail')
    ->setSyncMode(true);
// ->setUtoken($mevcutUtoken)  // aynı kullanıcıya ek kart için

$response = $client->cardStorage()->addCard($request);

sync_mode=0 için HTTP çağrısı SDK tarafından yapılmaz; toPayload($options) alıp $client->cardStorage()->getPaymentFormUrl() adresine form POST edin.

b) Kayıtlı kart listesi (CAPI LIST)

use Done\PayTR\Request\CardStorage\ListCardsRequest;

$listReq = (new ListCardsRequest())->setUtoken($utoken);
$listResp = $client->cardStorage()->listCards($listReq);

foreach ($listResp->cards as $kart) {
    // $kart->ctoken
    // $kart->last4
    // $kart->requireCvv (1 ise ödeme sırasında CVV iste)
}

c) Kayıtlı karttan ödeme

CAPI LIST’ten gelen require_cvv=1 ise kullanıcıdan CVV alıp gönderin.

use Done\PayTR\Request\CardStorage\PayWithRegisteredCardRequest;

$req = (new PayWithRegisteredCardRequest())
    ->setMerchantOid('SIP-' . uniqid())
    ->setPaymentAmount('75.00')
    ->setBuyer($buyer)
    ->setBasket($basket)
    ->setOkUrl('https://siteniz.com/ok')
    ->setFailUrl('https://siteniz.com/fail')
    ->setUtoken($utoken)
    ->setCtoken($ctoken)
    ->setRequireCvv(true)
    ->setCvv('000')
    ->setSyncMode(true);

$response = $client->cardStorage()->payWithRegisteredCard($req);

d) Kart silme (CAPI DELETE)

use Done\PayTR\Request\CardStorage\DeleteCardRequest;

$delReq = (new DeleteCardRequest())
    ->setUtoken($utoken)
    ->setCtoken($ctoken);

$client->cardStorage()->deleteCard($delReq);

e) Tekrarlayan ödeme (kayıtlı kart)

Kullanıcı etkileşimi olmadan tahsilat. non_3d=1, recurring_payment=1.

use Done\PayTR\Request\CardStorage\RecurringPaymentRequest;

$recReq = (new RecurringPaymentRequest())
    ->setMerchantOid('ABO-' . uniqid())
    ->setPaymentAmount('99.00')
    ->setBuyer($buyer)
    ->setBasket($basket)
    ->setOkUrl('https://siteniz.com/ok')
    ->setFailUrl('https://siteniz.com/fail')
    ->setUtoken($utoken)
    ->setCtoken($ctoken);

$recResp = $client->cardStorage()->recurringPayment($recReq);

if ($recResp->isSuccess()) {
    // Tahsilat başarılı
} elseif ($recResp->isWaitCallback()) {
    // Sonuç callback ile netleşecek
} else {
    // failed/error
    // $recResp->tryAgain === true ise, devam eden işlem var; daha sonra tekrar deneyin.
}

2) Bildirim URL (Callback) doğrulama

Iframe + Direkt API + Kart Saklama bildirimleri aynı doğrulama ile çalışır.

Zorunlu alanlar:

  • merchant_oid
  • status
  • total_amount
  • hash

Yanıt olarak yalnızca düz metin OK yazdırın. Öncesinde/sonrasında HTML veya ekstra çıktı olmamalı.

use Done\PayTR\Exceptions\SignatureException;

try {
    $notification = $client->callback()->verify($_POST);
} catch (SignatureException $e) {
    http_response_code(400);
    exit('Geçersiz bildirim.');
}

if ($notification->isSuccess()) {
    // siparisOnayla($notification->merchantOid, $notification->totalAmount);
} else {
    // siparisIptalEt($notification->merchantOid, $notification->failedReasonCode, $notification->failedReasonMsg);
}

echo $client->callback()->ok();
exit;

3) Durum sorgulama

use Done\PayTR\Request\Query\StatusRequest;

$request = (new StatusRequest())->setMerchantOid('SIPARIS-123');
$result = $client->query()->status($request);

// $result->paymentAmount, $result->paymentTotal, $result->currency, $result->paymentDate, $result->returns

4) İade

use Done\PayTR\Request\Refund\RefundRequest;

$request = (new RefundRequest())
    ->setMerchantOid('SIPARIS-123')
    ->setReturnAmount('11.97')
    ->setReferenceNo('IADE-REF-001');

$result = $client->refundCancel()->refund($request);

Hata Yönetimi

İstisna Açıklama
PayTRException Tüm SDK hatalarının tabanı
ValidationException Eksik/geçersiz parametre (örn. amount formatı, eksik callback alanı)
HttpException Bağlantı / zaman aşımı
SignatureException Callback hash uyuşmazlığı
ApiException PayTR status: failed/error; getReason(), getPayload()

Geriye Dönük Uyumluluk

Eski DTO tabanlı API çalışmaya devam eder; yeni kod için Request/Model stili önerilir.

  • iframePayment()->getToken(IframeTokenRequest)iframe()->createToken(CreateTokenRequest)
  • query()->query(QueryRequest)query()->status(StatusRequest)
  • refundCancel()->refund(RefundRequestDto) / refundWith(RefundRequest)refundCancel()->refund(RefundRequest)
  • UserBasket::encode()Model\Basket + BasketItem

Test

composer install
composer test

Gerçek PayTR çağrısı yapılmaz; testler FakeHttpClient ile çalışır.

Lisans

MIT. Bkz. LICENSE.

Referans