Search by

odemehub / php-sdk

odemehub

ödemehub ödeme geçidi için PHP istemcisi.

Package info

github.com/odemehub/php-sdk

pkg:composer/odemehub/php-sdk

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-09-29 11:22 UTC

This package is not auto-updated.

Last update: 2026-09-30 09:38:20 UTC


README

ödemehub ödeme geçidini kendi uygulamanızdan kullanmak için hazırlanmış PHP istemcisi. Kart çekmek, 3D ödeme başlatmak, müşteriyi ödeme sayfasına yollamak, ürün kataloğunuzu eşlemek, abonelik açmak, kart saklamak, iade ve iptal yapmak ve bir kartın taksit seçeneklerini sormak için gereken her şey burada.

İstemci her isteği takımınızın gizli anahtarıyla imzalar, gelen her yanıtın imzasını doğrular. Siz imza, başlık ya da JSON ayrıntılarıyla uğraşmazsınız.

Kurulum

PHP 8.2 ve üzeri gerekir.

composer require odemehub/php-sdk

Yapılandırma

Dört bilgiye ihtiyacınız var. Hepsi paneldeki Entegrasyon sayfasındadır (menünün en altında): API anahtarı, gizli anahtar, Çalışma Alanı Kimliğiniz ve kanallarınızla ödeme hesaplarınızın token'ları.

use Gurmehub\Odemehub\Client;
use Gurmehub\Odemehub\Options;

$client = new Client(new Options(
    baseUrl: 'https://odeme.gurmehub.com',
    team: '4829301756',              // Çalışma Alanı Kimliğiniz
    channelToken: '6f1c2e7a-4b3d-4c8e-9a61-2f5d7b0c3e14', // müşterinin size ulaştığı kanal
    apiKey: getenv('ODEMEHUB_API_KEY'),
    apiSecret: getenv('ODEMEHUB_API_SECRET'),
));

Gizli anahtar hiçbir zaman tel üzerinden gitmez; yalnızca imza üretmekte kullanılır. Anahtarları kodun içine yazmayın, ortam değişkeninde tutun.

Kanal token'ı entegrasyonun tamamı için bir kez verilir. Birden çok kanalda satıyorsanız tek bir istekte channelToken vererek o isteği başka kanala yazdırabilirsiniz. Geçit hiçbir yerde veritabanı numarası kullanmaz: kanal, ödeme hesabı, işlem, kayıtlı kart, abonelik ve sipariş her zaman token'ıyla adlanır.

Karttan doğrudan çekim

Müşteriyi bankasına göndermeden çekim yapar. Başarılı yanıt, paranın alındığı anlamına gelir.

use Gurmehub\Odemehub\Request\{Card, Customer, RegularPayment};

$payment = $client->regularPayment(new RegularPayment(
    channelReference: 'SIP-10231',          // işlemin sizdeki referansı
    amount: '450.00',
    installmentNumber: 1,
    ip: $_SERVER['REMOTE_ADDR'],
    customer: new Customer(
        channelReference: 'musteri-88',
        firstname: 'Ahmet',
        lastname: 'Yılmaz',
        email: 'ahmet@ornek.com',
        phone: '05551112233',
        address: 'Kızılırmak Mah. Dumlupınar Blv. No:3',
        district: 'Çankaya',
        province: 'Ankara',
        country: 'Türkiye',
    ),
    card: new Card(
        holderName: 'AHMET YILMAZ',
        number: '5400360000000003',
        expiryMonth: '12',
        expiryYear: '2030',
        securityCode: '000',
    ),
));

if ($payment->result->successful) {
    // $payment->transactionToken — ödemenin geçitteki token'ı; iade ve iptalde bununla adlandırılır
}

3D ödeme

3D'de çekim iki adımdır: siz ödemeyi başlatırsınız, müşteri bankasına gider, banka sonucu sizin adresinize gönderir.

use Gurmehub\Odemehub\Request\SecurePayment;

$payment = $client->securePayment(new SecurePayment(
    channelReference: 'SIP-10232',
    amount: '450.00',
    installmentNumber: 1,
    ip: $_SERVER['REMOTE_ADDR'],
    callbackUrl: 'https://magazam.com/odeme/donus',
    customer: $customer,
    card: $card,
));

if ($payment->result->successful) {
    header('Location: '.$payment->redirectUrl);   // müşteriyi bankaya gönderin
}

Başarılı yanıt ödeme alındı demek değildir; yalnızca müşterinin gideceği adres hazır demektir.

Müşteriyi 15 dakika içinde bu adrese yönlendirin. Sayfası o süre içinde açılmayan ödemenin süresi dolar (expired). Süresi dolmuş bağlantıyı açan müşteri doğrudan callbackUrl adresinize, successful=0 ile geri gönderilir; retrievePayment() sorgusu da başarısız sonucu ve nedenini döner.

Banka işini bitirince müşteri, tarayıcısı üzerinden callbackUrl adresinize döner. O POST sonucu taşımaz, yalnızca sonucun hazır olduğunu haber verir:

Alan Anlamı
transaction_token ödemenin geçitteki token'ı
channel_reference sizin kendi referansınız
successful 1 / 0 — yalnızca ipucu, güvenilmez

Sonucu kendi imzalı bağlantınızdan sorun:

use Gurmehub\Odemehub\Request\RetrievePayment;

$outcome = $client->retrievePayment(new RetrievePayment(
    transactionToken: $_POST['transaction_token'],
));

if ($outcome->result->successful) {
    // siparişi ödendi olarak işaretleyin
}

Neden böyle: o POST'u bizim sunucumuz değil, müşterinin tarayıcısı gönderir; tarayıcıya imzalayacak bir sır verilemez. successful alanına bakıp sipariş kapatmayın — onu herkes gönderebilir; yalnız "başarısız" ipucunda gereksiz sorgudan kaçınmak için kullanın. Geçide sorduğunuz yanıt ise her zaman imzalıdır ve SDK imzayı sizin için doğrular. Başkasının işlemini sorarsanız ValidationException alırsınız.

Ürünler

Sipariş kalemleri ve abonelikler ürünleri sizdeki referanslarıyla adlandırır. Ürünü panelde (Ürünler sayfası) tanımlayabilir ya da kendi kataloğunuzdan geçide yazabilirsiniz:

use Gurmehub\Odemehub\Request\SaveProduct;

$product = $client->saveProduct(new SaveProduct(
    channelReference: 'KAHVE-MAKINESI',
    name: 'Kahve makinesi',
    type: 'simple',          // simple | recurring
    amount: '450.00',
    taxRate: '20',           // fiyatın içindeki KDV oranı
));

$client->saveProduct(new SaveProduct(
    channelReference: 'PREMIUM-AYLIK',
    name: 'Premium üyelik',
    type: 'recurring',
    amount: '149.90',
    taxRate: '20',
    period: 'monthly',       // monthly | yearly — yalnız recurring için zorunlu
));

Aynı kanalda aynı referans aynı üründür: tekrar gönderirseniz ikinci ürün açılmaz, mevcut olan güncellenir. currency verilmezse TRY, isActive verilmezse true kabul edilir. Ürün silinmez; isActive: false ile satışa kapatılır.

Ödeme istekleri ürünü hiçbir zaman değiştirmez; ürünün tek yazıldığı yer bu çağrı ve panel.

Ödeme sayfası

Kart bilgisini hiç görmek istemiyorsanız sipariş açıp müşteriyi geçidin kendi sayfasına yollayabilirsiniz.

use Gurmehub\Odemehub\Request\{OrderItem, OrderPayment};

$order = $client->orderPayment(new OrderPayment(
    channelReference: 'SIPARIS-10233',
    successUrl: 'https://magazam.com/tesekkurler',
    cancelUrl: 'https://magazam.com/sepet',
    customer: $customer,
    items: [
        new OrderItem(channelReference: 'KAHVE-MAKINESI'),
        new OrderItem(channelReference: 'KAHVE-500G', quantity: 2, unitAmount: '180.00'),
        new OrderItem(channelReference: 'HEDIYE-PAKETI', name: 'Hediye paketi', unitAmount: '25.00'),
    ],
));

header('Location: '.$order->checkoutUrl);

Sipariş tutarını göndermezsiniz; geçit kalemleri toplar ve $order->amount olarak döner. Bir kalemin boş bıraktığı ad, fiyat ve KDV oranı kayıtlı üründen gelir; kalemde verdiğiniz değerler yalnızca o sipariş için geçerlidir, ürünü değiştirmez. Kayıtlı olmayan bir referansla da kalem gönderebilirsiniz, ama o zaman name ve unitAmount zorunludur.

Ödeme tamamlanınca müşteri, 3D'dekiyle aynı biçimde successUrl adresinize döner: aynı üç alan gelir, sonucu yine retrievePayment() ile sorarsınız. Müşteri ödeme sayfasında karttan kaynaklı bir hata alırsa size dönmez, sayfada kalıp başka kartla dener.

Abonelikler

Müşteriden dönem dönem tahsilat yapmak için abonelik açarsınız. Neye abone olunduğu bir ya da birkaç abonelik ürünüdür (type: 'recurring'), sizdeki referanslarıyla adlandırılır; fiyatı, para birimini ve dönemini ürün taşır. Aynı aboneliğe konan ürünlerin dönemi ve para birimi aynı olmalıdır.

use Gurmehub\Odemehub\Request\{SubscriptionItem, SubscriptionPayment};

$subscription = $client->subscriptionPayment(new SubscriptionPayment(
    channelReference: 'UYELIK-4471',
    items: [
        new SubscriptionItem(channelReference: 'PREMIUM-AYLIK'),
        new SubscriptionItem(channelReference: 'EK-KULLANICI', quantity: 3),
    ],
    successUrl: 'https://magazam.com/tesekkurler',
    customer: $customer,
));

$subscription->token; // aboneliği sonra sorgulamak ve iptal etmek için saklayın

header('Location: '.$subscription->checkoutUrl);

Bir kaleme unitAmount verirseniz o fiyat yalnızca ilk dönem için geçerlidir (ör. ilk ay yarı fiyat); sonraki dönemler ürünün kendi fiyatından çekilir.

İlk ödeme her zaman geçidin kendi sayfasında yapılır ve kart zorunlu olarak saklanır: sonraki dönemler o karttan çekilir. Ödeme tamamlanınca müşteri successUrl adresinize döner ve sonucu yine retrievePayment() ile sorarsınız; abonelik active olur ve aşağıdaki bildirim de gider.

Dönem bitince yeni dönem açılır ve müşterinin varsayılan kartından çekilir. Banka kabul etmezse çekim bir buçuk gün içinde beş kez denenir (araları 3, 6, 9 ve 12 saat); bu sırada abonelik active kalır. Beşinci deneme de olmazsa abonelik past_due olur; çalışma alanı yöneticilerinize e-posta, webhookUrl adresinize bildirim gider. İkisi de o dönemin dilediği kartla ödenebileceği bağlantıyı taşır; bağlantıyı müşterinize siz iletirsiniz. Süre sınırı yoktur; müşteri ödediği anda abonelik kaldığı yerden devam eder.

Aboneliğin durumunu sorabilirsiniz:

use Gurmehub\Odemehub\Request\RetrieveSubscription;

$subscription = $client->retrieveSubscription(new RetrieveSubscription(subscriptionToken: $token));

echo $subscription->status;      // pending | active | past_due | cancelled
echo $subscription->amount;      // 149.90 — içinde bulunulan dönemin fiyatı
echo $subscription->endsAt;      // sonraki tahsilat zamanı
echo $subscription->checkoutUrl; // ödenmemiş dönem varsa müşteriye verilecek adres

foreach ($subscription->items as $item) {
    echo "{$item->quantity} x {$item->name} ({$item->channelReference})";
}

if ($subscription->isPastDue()) {
    // müşteriyi kendi ödeme sayfanızda uyarabilirsiniz
}

Tutar, aboneliğin içinde bulunduğu dönemin fiyatıdır. Ürünün fiyatını yükseltirseniz yürüyen dönem çekildiği fiyatta kalır, yeni fiyat sonraki dönemden itibaren işler.

İptalde ödenmiş günler yanmaz:

use Gurmehub\Odemehub\Request\CancelSubscription;

$subscription = $client->cancelSubscription(new CancelSubscription(subscriptionToken: $token));

$subscription->cancelledAt;  // iptal edildiği an
$subscription->endsAt;       // hizmetin süreceği son gün
$subscription->isCancelled(); // ödenmiş dönem sürüyorsa henüz false

Müşteri, ödediği dönemin sonuna kadar hizmeti almaya devam eder; o güne kadar abonelik active görünür, dönem bitince cancelled olur ve bir daha tahsilat yapılmaz. Ödenmemiş bir aboneliğin (ilk ödemesi yapılmamış ya da past_due) iptali hemen geçerlidir. İade yapılmaz.

Aboneliğin açılabilmesi için varsayılan ödeme hesabınızın kart saklayabiliyor olması gerekir; saklamayan bir hesapla açmaya çalışırsanız istek subscription.payment_provider_token alanında reddedilir.

Abonelik bildirimleri (webhook)

Abonelik açarken webhookUrl verirseniz, aboneliğin durumu her değiştiğinde o adrese imzalı bir POST gönderilir. Gövde düz JSON'dur ve imza X-Signature başlığındadır — yani geçidin API yanıtlarıyla aynı yöntem.

use Gurmehub\Odemehub\Request\{SubscriptionItem, SubscriptionPayment};

$subscription = $client->subscriptionPayment(new SubscriptionPayment(
    channelReference: 'UYELIK-4471',
    items: [new SubscriptionItem(channelReference: 'PREMIUM-AYLIK')],
    successUrl: 'https://magazam.com/tesekkurler',
    customer: $customer,
    webhookUrl: 'https://magazam.com/odemehub/abonelik',
));

Bildirimi karşılayan uçta gövdeyi ham okuyup imzayla birlikte SDK'ya verin:

use Gurmehub\Odemehub\Exception\SignatureException;

try {
    $webhook = $client->subscriptionWebhook(
        file_get_contents('php://input'),
        $_SERVER['HTTP_X_SIGNATURE'] ?? null,
    );
} catch (SignatureException $exception) {
    http_response_code(400);
    exit;
}

$subscription = $webhook->subscription;   // sorgudakiyle aynı nesne

match (true) {
    $webhook->isActive() => aboneligiAc($subscription->channelReference, $subscription->endsAt),
    $webhook->isPastDue() => musteriyiUyar($subscription->checkoutUrl),
    $webhook->isCancelled() => yenilemeyiDurdur($subscription->endsAt),
    $webhook->isEnded() => erisimiKapat($subscription->channelReference),
};

http_response_code(200);

Gönderilen olaylar aboneliğin durumudur, yapılan işlem değil:

Olay Ne zaman gider
active bir dönem ödendi (ilk ödeme ya da yenileme)
past_due dönem kayıtlı karttan tahsil edilemedi, müşteriden bekleniyor
cancelled abonelik iptal edildi; müşteri endsAt tarihine kadar hizmeti almaya devam eder
ended ödenmiş dönem doldu, abonelik kapandı

2xx dışında bir yanıt (ya da yanıtsızlık) başarısız sayılır; bildirim 5 dakika sonra bir kez daha denenir. Ulaşmayan bildirimler panelde aboneliğin sayfasında HTTP kodu ve yanıtıyla listelenir.

Ödeme hangi hesaptan geçer

paymentProviderToken verirseniz ödeme o hesaptan geçer; sipariş ve abonelik açarken de aynı parametre vardır ve müşteri ödeme sayfasında o hesaptan öder. Vermezseniz hesabı çalışma alanınız seçer: panelde Ödeme Ayarları → Gate (Yönlendirme) altındaki kurallar sırayla denenir ve ödemenin karşıladığı ilk kural hesabı belirler. Kurallar kartın bankasına, şemasına, programına, tipine, ticari kart olup olmadığına, tutara ve para birimine bakabilir. Hiçbir kural tutmazsa ödeme varsayılan hesaptan geçer.

  • Kuralın hesabı ödemeyi alamıyorsa (ödeme türünü ya da para birimini desteklemiyorsa) o kural atlanır.
  • Kayıtlı kartla ödeme her zaman kartın saklandığı hesaptan geçer.
  • Taksitleri retrieveBin() ile gösteriyorsanız orada da hesap vermeyin: taksitler ödemenin gideceği hesaptan gelir ve çekilen tutar gösterdiğinizle aynı olur.

Kur çevirisi

Panelde Ödeme Ayarları → Kur Çevirici altında bir kural tanımladıysanız, o para biriminde gelen ödeme karttan kuralın para biriminde çekilir. Örneğin 100 USD istersiniz, karttan 4.985,56 TRY çekilir. Kur, TCMB'nin güncel döviz satış kuru ve üzerine eklediğiniz marjdır ya da sizin girdiğiniz sabit kurdur.

İsteğinizde hiçbir şey değişmez: tutarı ve para birimini her zamanki gibi gönderirsiniz. Yanıttaki conversion karttan ne çekildiğini söyler:

$payment = $client->regularPayment(new RegularPayment(
    amount: '100.00',
    currency: 'USD',
    // ...
));

if ($payment->conversion !== null) {
    echo $payment->conversion->amount;   // 4985.56
    echo $payment->conversion->currency; // TRY
    echo $payment->conversion->rate;     // 49.855560
}
  • Çevrilmeyen ödemede conversion null gelir. retrievePayment() aynı bilgiyi yeniden verir.
  • İade tutarını çekilen para biriminde gönderin (yukarıdaki örnekte TRY).
  • Güncel kur alınamıyorsa ödeme alınmaz; 422 ile transaction.currency alanında hata döner. Birkaç dakika sonra tekrar deneyin.

Kart sorgusu ve taksitler

Kart numarasının ilk hanelerinden kartın kim tarafından verildiğini, hangi programa ait olduğunu ve tutarın kaç taksite bölünebileceğini sorar. Hiçbir şey çekilmez.

use Gurmehub\Odemehub\Request\RetrieveBin;

$bin = $client->retrieveBin(new RetrieveBin(bin: '54003600', amount: '450.00'));

if ($bin->result->successful) {
    echo $bin->issuerName;     // Garanti Bankası
    echo $bin->program;        // Bonus
    echo $bin->scheme;         // mastercard
    echo $bin->type;           // credit
    var_dump($bin->isCommercial);

    foreach ($bin->installments as $installment) {
        // 3 taksitte ayda 157.87, toplam 473.60
        echo "{$installment->number} x {$installment->amount} = {$installment->total}";
    }
}

Kartın tamamını göndermeyin; ilk 6-8 hane yeter ve yalnızca o kadarı kabul edilir.

Sorgu başarısız dönebilir: kart tanınmıyor olabilir ya da hesabınızın sağlayıcısı taksit vermiyor olabilir. İki durumda da satışı durdurmayın, tek çekimle devam edin.

Tutarlar ve taksit

İki tutar vardır ve karıştırılmamalıdır:

Alan Anlamı
amount Karttan çekilecek tutar. Vade farkı varsa içindedir.
baseAmount Sattığınız tutar, vade farkından önceki hâli. Gönderilmezse amount ile aynı kabul edilir.

Taksit yalnızca Türk Lirası ödemelerde yapılır. USD, EUR ya da GBP ödemede installmentNumber 1 olmalıdır ve retrieveBin() taksit listesini boş döner; kur çevirisiyle TRY'den başka bir para birimine çekilen ödeme için de aynısı geçerlidir.

Taksitsiz satışta ikisi eşittir ve baseAmount göndermenize gerek yoktur. Taksitli satışta retrieveBin size o taksidin toplamını verir; onu amount olarak, sattığınız tutarı baseAmount olarak gönderin:

$payment = $client->regularPayment(new RegularPayment(
    channelReference: 'SIP-10234',
    amount: '473.60',        // 3 taksitin toplamı
    baseAmount: '450.00',    // satılan tutar
    installmentNumber: 3,
    // ...
));

Bazı sağlayıcılar vade farkını kendileri ekler; geçit bunu bilir ve gerekirse sağlayıcıya taban tutarı gönderir. Sizin tarafınızda değişen bir şey yoktur.

Kayıtlı kartlar

Müşterinin kartını saklayıp sonraki ödemelerde numara sormadan çekim yapabilirsiniz.

use Gurmehub\Odemehub\Request\{DefaultSavedCard, DeleteSavedCard, NamedCustomer, SaveCard, SavedCards};

// Ödeme sırasında saklamak için: Card nesnesine shouldSave: true verin.
// Ödeme olmadan saklamak için:
$kept = $client->saveCard(new SaveCard(customer: $customer, card: $card));

$musteri = new NamedCustomer(channelReference: 'musteri-88');

// Müşterinin kartları
$cards = $client->savedCards(new SavedCards(customer: $musteri));

// Varsayılan yapma / silme
$token = $cards->savedCards[0]->token;

$client->defaultSavedCard(new DefaultSavedCard(customer: $musteri, savedCardToken: $token));
$client->deleteSavedCard(new DeleteSavedCard(customer: $musteri, savedCardToken: $token));

Kayıtlı kartla ödeme alırken card yerine kartın token'ını verin:

$payment = $client->regularPayment(new RegularPayment(
    channelReference: 'SIP-10235',
    amount: '120.00',
    installmentNumber: 1,
    ip: $_SERVER['REMOTE_ADDR'],
    customer: $customer,
    savedCardToken: $token,
));

Kart saklayan bir ödemenin yanıtında $payment->savedCard dolu gelir; kartın token'ını oradan öğrenirsiniz. Kart her yerde token ile adlandırılır.

İade ve iptal

use Gurmehub\Odemehub\Request\{CancelPayment, RefundPayment};

// Gün sonu almamış ödemenin tamamını geri alır
$client->cancelPayment(new CancelPayment(transactionToken: $payment->transactionToken));

// Tutar verilirse kısmi, verilmezse kalanın tamamı iade edilir.
// Kur çevirisiyle çekilen ödemede tutar çekilen para birimindedir.
$client->refundPayment(new RefundPayment(transactionToken: $payment->transactionToken, amount: '100.00'));

Hatalar

Bütün istisnalar OdemehubException'dan türer; tek bir catch hepsini yakalar.

İstisna Ne demek
ValidationException Gönderdiğiniz alanlar kabul edilmedi. Ödeme denenmedi. $e->errors alan alan söyler.
AuthenticationException API anahtarı bu takıma ait değil ya da imza gizli anahtarla tutmuyor.
SignatureException Gelen yanıtın ya da bildirimin imzası tutmadı. Geçitten geldiği kanıtlanamaz; işleme almayın.
TransportException Geçide ulaşılamadı ya da yanıt okunamadı. Ödemenin ne olduğu belirsizdir; geçitteki kayıt asıl doğruyu söyler.
UnexpectedResponseException Beklenmeyen bir yanıt geldi.

Ağ hatasında ödemeyi körlemesine tekrarlamayın: TransportException "olmadı" demek değil, "bilmiyorum" demektir.

Örnekler

example/ klasöründe çalışan küçük sayfalar var: kart çekimi, 3D, ödeme sayfası, kart sorgusu, kayıtlı kartlar, iade. Kendi anahtarlarınızı ortam değişkeniyle verip tarayıcıda açabilirsiniz:

cd example
ODEMEHUB_BASE_URL=https://odeme.gurmehub.com ODEMEHUB_TEAM=4829301756 ODEMEHUB_CHANNEL_TOKEN=6f1c2e7a-4b3d-4c8e-9a61-2f5d7b0c3e14 ODEMEHUB_API_KEY=... ODEMEHUB_API_SECRET=... php -S localhost:8080

Sonra http://localhost:8080/index.php adresini açın. 3D denemesi yapacaksanız bankanın döneceği adresi de verin: ODEMEHUB_CALLBACK_URL=http://localhost:8080/return.php.

Ortam değişkeni vermezseniz örnekler yerel geliştirme kurulumuna bağlanır.