Ppospaybu
API Anahtarları← Panele Dön

Entegrasyon Kılavuzu

API Dokümantasyonu

Kendi sitenizden ödeme almak için iki yöntem: kartı kendi sunucunuzdan gönderdiğiniz Direkt API, ve kart verisinin hiç size dokunmadığı iframe / Hosted Checkout. Tüm uçlar JSON döner ve API anahtarınızla doğrulanır.

API Kimliği tanımlı değil

“Test Et” panellerini kullanmak için pk_/sk_ anahtarınızı girin.

Genel Bakış

Base URL
https://pospaybu.com/api/v1

Makine-okunur sözleşme — IDE/Postman'e aktarın:

iframe / Hosted Checkout

Kart bilgisi yalnızca bizde işlenir (PCI SAQ-A). Hızlı, düşük yük. Önerilen.

Direkt API (S2S)

Kartı kendiniz toplar, sunucudan gönderirsiniz. Tam kontrol; ham kart → PCI SAQ-D sorumluluğu sizdedir.

Ödeme Akışları

Satış akışı (tek adım)

POST /v1/payments (pre_auth: false)PAIDSETTLED

Kullanım: anında teslim edebildiğinizde (dijital ürün, abonelik vb.).

Provizyon + Kapama akışı (iki adım)

POST /v1/payments (pre_auth: true)AUTHORIZEDCAPTURED

Kullanım: tahsilattan önce stok/teslimat doğrulamanız gerektiğinde.

Kimlik Doğrulama

Her isteğe API anahtarınızı iki başlıkla ekleyin. Anahtarları API Anahtarları ekranından oluşturursunuz; gizli anahtar (sk_) yalnızca bir kez gösterilir.

X-Api-Key: pk_xxx
X-Api-Secret: sk_xxx

Eksik/geçersiz kimlikte 401 döner.

iframe / Hosted Checkout (önerilen)

Önce bir oturum oluşturun, dönen checkout_url'i ya iframe ile gömün ya da tam sayfa yönlendirin. Kart verisi tarafınıza hiç gelmez; 3D, taksit, yemek kartı ve marka/tema otomatik gelir.

POST/v1/checkout/sessions
İstek
curl -X POST https://pospaybu.com/api/v1/checkout/sessions \
  -H "X-Api-Key: pk_xxx" \
  -H "X-Api-Secret: sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 149.90,
    "currency": "TRY",
    "reference": "SIPARIS-1001",
    "callback_url": "https://siteniz.com/odeme/sonuc",
    "customer": { "email": "ahmet@ornek.com", "phone": "5320000000", "code": "CARI-1001" },
    "metadata": { "order_id": 1001 }
  }'
Yanıt
HTTP/1.1 201 Created
{
  "token": "Hh3k...long-unguessable-token",
  "reference": "SIPARIS-1001",
  "checkout_url": "https://pospaybu.com/checkout/Hh3k...long-unguessable-token",
  // Uygulanan koşul grubu; grup gönderilmediyse ve varsayılan yoksa null.
  "condition_group": { "id": 7, "name": "Kampanya", "slug": "kampanya" }
}

Ödeme koşulu grubu seçimi (opsiyonel)

Oturumu belirli bir ödeme koşulu grubuna bağlayabilirsiniz: sayısal condition_group_id veya grup condition_group (slug). O gruba tanımlı taksit / komisyon / POS kuralları uygulanır. Göndermezseniz varsayılan grup (tanımlıysa), o da yoksa genel koşullar geçerlidir. Yanıttaki condition_group hangi grubun uygulandığını (id, name, slug) veya null döner. Grupları Ödeme Linkleri / Koşul Grupları ekranından yönetirsiniz.

İstek (grup ile)
curl -X POST https://pospaybu.com/api/v1/checkout/sessions \
  -H "X-Api-Key: pk_xxx" \
  -H "X-Api-Secret: sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 149.90,
    "currency": "TRY",
    "reference": "SIPARIS-1001",
    "callback_url": "https://siteniz.com/odeme/sonuc",
    "condition_group_id": 7
  }'
# ── ya da slug ile: ────────────────────────────────────────────
#   "condition_group": "kampanya"
#
# İkisi de opsiyoneldir. Göndermezseniz varsayılan grup (tanımlıysa),
# o da yoksa genel koşullar (tüm taksit/komisyon/POS kuralları) uygulanır.

Sayfayı gömün

HTML
<!-- Ödeme sayfasını sitenize iframe olarak gömün -->
<iframe
  src="CHECKOUT_URL_BURAYA"
  width="100%" height="720"
  style="border:0; max-width:480px"
  allow="payment">
</iframe>

<!-- ya da tam sayfa yönlendirme: -->
<a href="CHECKOUT_URL_BURAYA">Ödemeye Geç</a>

Müşteri ödemeyi tamamlayınca bizim sonuç ekranımızda kalır; sitenize otomatik tarayıcı yönlendirmesi yapılmaz (#332). Ödeme sonucunu callback_url'inize gönderdiğimiz imzalı server-to-server POST ile alır, kesin durumu isterseniz webhook ya da durum sorgusu ile pekiştirirsiniz.

Oturum oluştururken customer (telefon, e-posta, cari kodu + Ayarlar > Müşteri Bilgileri'nde tanımladığınız alanlar) gönderebilirsiniz; göndermezseniz bu bilgiler ödeme sayfasında ödemeyi yapan kişiden istenir ve cari kaydına bağlanır.

Direkt API — Kart ile Ödeme (S2S)

Kartı kendi formunuzda toplar, sunucunuzdan gönderirsiniz. POS yönlendirme, taksit, fraud ve gerekirse failover otomatik uygulanır.

POST/v1/payments
AlanTipAçıklama
amountnumberTahsil edilecek tutar (zorunlu).
currencystringISO-4217, varsayılan TRY.
installment_countintTaksit sayısı (1 = tek çekim).
pre_authbooltrue → provizyon (blokede tut, sonra kapat).
cardobjectnumber, holder, exp_month, exp_year, cvv (zorunlu).
customerobjectname, email, phone, code (cari kodu), ip + ayarladığınız özel alanlar. code/email ile cari kaydına bağlanır.
callback_urlurl3D sonrası müşterinin döneceği adres.
referencestringSipariş numaranız (verilmezse üretilir).
metadataobjectİstediğiniz ek alanlar.

İstek örneği

curl -X POST https://pospaybu.com/api/v1/payments \
  -H "X-Api-Key: pk_xxx" \
  -H "X-Api-Secret: sk_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: SIPARIS-1001" \
  -d '{
    "reference": "SIPARIS-1001",
    "amount": 149.90,
    "currency": "TRY",
    "installment_count": 1,
    "pre_auth": false,
    "card": {
      "number": "4355084355084358",
      "holder": "AHMET YILMAZ",
      "exp_month": "12",
      "exp_year": "30",
      "cvv": "000"
    },
    "customer": {
      "name": "Ahmet Yılmaz",
      "email": "ahmet@ornek.com",
      "phone": "5320000000",
      "code": "CARI-1001",
      "ip": "1.2.3.4"
    },
    "callback_url": "https://siteniz.com/odeme/sonuc",
    "metadata": { "order_id": 1001 }
  }'
Yanıt
HTTP/1.1 201 Created
{
  "data": {
    "id": 42,
    "reference": "SIPARIS-1001",
    "method": "card",
    "status": "paid",            // paid | pending(3D) | failed | authorized(provizyon)
    "amount": "149.90",
    "currency": "TRY",
    "installment_count": 1,
    "capture_mode": "auto",
    "net_amount": 149.90,
    "transactions": [
      { "type": "sale", "provider": "garanti", "provider_ref": "...", "status": "success" }
    ]
  }
}

3D Secure akışı

JavaScript
// status === "pending" → 3D Secure gerekiyor.
const meta = res.data.metadata;
if (res.data.status === "pending") {
  if (meta.redirect_html) {
    // Bankaya otomatik POST eden formu müşterinin tarayıcısında render edin:
    document.open(); document.write(meta.redirect_html); document.close();
  } else if (meta.redirect_url) {
    window.location.href = meta.redirect_url;
  }
}
// 3D bitince müşteri BİZİM sonuç ekranımızda kalır (artık sitenize otomatik
// tarayıcı yönlendirmesi YAPILMAZ, #332). Sonucu callback_url'inize gönderdiğimiz
// imzalı server-to-server POST (payment.paid / payment.failed) ile alın.

Taksit Sorgulama

Bir tutar için geçerli taksit seçeneklerini ve müşteriye yansıyan toplamı döner.

GET/v1/installments?amount=&card_brand=&virtual_pos_id=
cURL
curl "https://pospaybu.com/api/v1/installments?amount=1000&card_brand=visa" \
  -H "X-Api-Key: pk_xxx" -H "X-Api-Secret: sk_xxx"

// Yanıt:
{ "data": [
  { "installment_count": 1, "card_brand": null,   "commission_rate": "0.00", "total": 1000, "monthly": 1000 },
  { "installment_count": 3, "card_brand": "visa", "commission_rate": "4.50", "total": 1045, "monthly": 348.33 }
] }

Ödeme Durumu Sorgulama

Referansınızla ödemenin güncel durumunu çekin (webhook'a alternatif/pekiştirici).

GET/v1/payments/{reference}
cURL
curl "https://pospaybu.com/api/v1/payments/SIPARIS-1001" \
  -H "X-Api-Key: pk_xxx" -H "X-Api-Secret: sk_xxx"
// → PaymentResource (status: paid | pending | failed | authorized | refunded)

İade (Refund)

Başarılı bir ödemeyi tam veya kısmi iade edin. Birden çok kısmi iade toplanır.

POST/v1/payments/{reference}/refund
cURL
# Tam iade: body boş. Kısmi iade: "amount" gönderin.
curl -X POST https://pospaybu.com/api/v1/payments/SIPARIS-1001/refund \
  -H "X-Api-Key: pk_xxx" -H "X-Api-Secret: sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 50.00 }'

Provizyon (Ön Provizyon / Pre-Auth)

Tutarı kartta bloke edip (provizyon) daha sonra tahsil etmek için direkt ödemede pre_auth: true gönderin. Desteklenen sağlayıcılar: tüm banka POS'ları ve Craftgate.

cURL
# Provizyon (ön provizyon): tutarı blokede tut, sonra kapat.
curl -X POST https://pospaybu.com/api/v1/payments \
  -H "X-Api-Key: pk_xxx" -H "X-Api-Secret: sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 500, "pre_auth": true, "card": { ... } }'
# → data.status = "authorized" (henüz tahsil edilmedi).
# Provizyon kapama / iptal: panel > Ödemeler ekranı
# ("Provizyonu Kapat" / "Provizyonu İptal Et").

Yemek Kartı ile Ödeme

Yemek kartı entegrasyonunuzu tanımladıktan sonra bakiye sorgulayıp tahsilat alın.

POST/v1/meal-payments/balance
POST/v1/meal-payments
cURL
# Bakiye sorgu
curl -X POST https://pospaybu.com/api/v1/meal-payments/balance \
  -H "X-Api-Key: pk_xxx" -H "X-Api-Secret: sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "meal_card_id": 1, "card": { "number": "6060...." } }'

# Ödeme (Multinet / Sodexo-Pluxee / Edenred-Ticket / Setcard / Metropol)
curl -X POST https://pospaybu.com/api/v1/meal-payments \
  -H "X-Api-Key: pk_xxx" -H "X-Api-Secret: sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{ "meal_card_id": 1, "amount": 80, "reference": "SIP-1002",
        "card": { "number": "6060....", "pin": "1234" } }'

Bakiye sorgu

Yemek kartı ile ödeme

Tüm Uçlar

MethodEndpointAçıklama
POST/v1/paymentsKart ile ödeme (Direkt / S2S)
POST/v1/checkout/sessionsHosted / iframe oturumu oluştur
GET/v1/installmentsTaksit seçeneklerini sorgula
GET/v1/payments/{reference}Ödeme durumunu sorgula
POST/v1/payments/{reference}/refundİade (tam / kısmi)
POST/v1/meal-payments/balanceYemek kartı bakiye sorgu
POST/v1/meal-paymentsYemek kartı ile ödeme
POST/api/callbacks/{provider}3D Secure dönüşü (bankalar çağırır)

Provizyon kapama / iptal ve durum-sorgu (sync) işlemleri panel üzerinden yapılır.

Webhook'lar

Ödeme durumu değişince Webhook'lar ekranında tanımladığınız uca imzalı bir bildirim göndeririz. Kayıtlı bir endpoint tüm olayları alır:

  • payment.authorized — provizyon alındı
  • payment.paid — tahsil edildi
  • payment.failed — başarısız
  • payment.refunded / payment.partially_refunded — iade

Her teslimat üç başlık taşır: imza (X-Pospaybu-Signature), olay (X-Pospaybu-Event) ve tekilleştirme kimliği (X-Pospaybu-Delivery). Zarf sabittir: { id, event, created_at, data }.

Gelen istek
POST https://siteniz.com/webhook
X-Pospaybu-Signature: t=1717500000,v1=9f86d08...   (HMAC-SHA256)
X-Pospaybu-Event:     payment.paid
X-Pospaybu-Delivery:  12345                        (tekilleştirme id'si)

{
  "id": 12345,                                  // id + created_at retry'larda SABİT
  "event": "payment.paid",                      //   → aynı teslimatı idempotent ayıklayın
  "created_at": "2026-06-05T10:00:00+00:00",
  "data": {
    "reference": "SIPARIS-1001",
    "status": "paid",
    "amount": 149.90,
    "refunded_amount": 0,
    "currency": "TRY",
    "installment_count": 1,
    "method": "card",
    "paid_at": "2026-06-05T10:00:00+00:00",
    "transaction_id": 84,                        // opsiyonel (son işlem kimliği)
    "metadata": { "order_id": 1001 }
  }
}

payment.failed olayında data ek olarak banka hata bilgisini içerir (PCI: kart/PAN/CVV taşınmaz):

payment.failed (ek alanlar)
"event": "payment.failed",
"data": {
  "reference": "SIPARIS-1001",
  "status": "failed",
  "amount": 149.90,
  ...
  "error_code": "51",
  "error_message": "Yetersiz bakiye"
}

Gövdeyi ham (raw) haliyle imzalayın; imza t=<unix>,v1=HMAC-SHA256("t.rawBody", secret) biçimindedir ve secret webhook endpoint'i oluştururken bir kez gösterilir. Zaman toleransı 300 sn'dir; başarısız teslimatlar üstel gecikmeyle yeniden denenir; id/created_at sabit kaldığından aynı teslimatı idempotent ayıklayın.

PHP
<?php
// İmza doğrulama (PHP) — secret webhook endpoint'i oluştururken bir kez gösterilir.
$payload = file_get_contents('php://input');
$header  = $_SERVER['HTTP_X_POSPAYBU_SIGNATURE'] ?? '';
$secret  = 'whsec_xxx';   // endpoint'in kendi secret'i

parse_str(str_replace(',', '&', $header), $p);   // t=..., v1=...
$expected = hash_hmac('sha256', ($p['t'] ?? '') . '.' . $payload, $secret);

if (! hash_equals($expected, $p['v1'] ?? '') || abs(time() - (int)($p['t'] ?? 0)) > 300) {
    http_response_code(400); exit('invalid signature');
}
$event = json_decode($payload, true);
// $event['id'] ile tekilleştirin, sonra 2xx dönün.
http_response_code(200);

Parçalı (split) ödeme: Bir sipariş birden çok karta/parçaya bölünse bile bildirim yalnızca parent tamamen tahsil olunca tek bir payment.paid olarak gider — parça başına ayrı webhook gönderilmez. Parça planı yalnızca checkout içindeki dahili bir görünümdür.

callback_url — Server-to-Server POST

Checkout oturumu / direkt ödeme oluştururken callback_url verdiyseniz, ödeme sonucunu o adrese imzalı, sunucudan sunucuya (S2S) POST ile göndeririz — kayıtlı bir webhook endpoint'i gerektirmeden. Bu, eskiden yapılan tarayıcı GET yönlendirmesinin (?reference=...&status=...) yerine geçer (#332): müşteri artık bizim sonuç ekranımızda kalır, tarayıcı sitenize yönlendirilmez.

callback_url yalnızca tamamlanma olaylarını alır: payment.paid, payment.authorized, payment.failed (iade olayları yalnız kayıtlı webhook endpoint'lerine gider). Payload şeması, zarf ve ilk üç başlık webhook ile aynıdır; ek olarak X-Pospaybu-Key başlığı imza anahtarının hangi API anahtarınızdan (pk_) türetildiğini bildirir.

Gelen istek
POST https://siteniz.com/odeme/sonuc          (checkout callback_url)
X-Pospaybu-Signature: t=1717500000,v1=4b2a9c...
X-Pospaybu-Event:     payment.paid
X-Pospaybu-Delivery:  12346
X-Pospaybu-Key:       pk_xxx     (imza anahtarının türetildiği API anahtarı)

{
  "id": 12346,
  "event": "payment.paid",              // yalnız paid | authorized | failed
  "created_at": "2026-06-05T10:00:00+00:00",
  "data": {
    "reference": "SIPARIS-1001",        // eski GET dönüşündeki reference + status
    "status": "paid",                   //   anahtarları burada da mevcuttur
    "amount": 149.90,
    "refunded_amount": 0,
    "currency": "TRY",
    "installment_count": 1,
    "method": "card",
    "paid_at": "2026-06-05T10:00:00+00:00",
    "metadata": { "order_id": 1001 }
  }
}

Geçiş notu (eski GET dönüşü)

Daha önce ?reference=...&status=paid|failed GET dönüşünü bekleyen entegrasyonlar için: aynı reference ve status anahtarları POST'un data gövdesinde de yer alır. Okuma yerinizi query string yerine POST gövdesine taşıyın.

İmza anahtarı türetme

Kayıtlı webhook'tan tek farkı budur: callback_url'in kendi secret'i yoktur; imza anahtarını kendi API secret'inizden (sk_) türetirsiniz. X-Pospaybu-Key ile eşleşen anahtarı seçin, sonra:

signingKey = HMAC_SHA256("pospaybu:callback_url:v1", sha256_hex(sk_...))

İmza doğrulaması bu signingKey ile webhook ile bire bir aynıdır (t.rawBody üzerinde HMAC, 300 sn tolerans, hash_equals).

PHP
<?php
// callback_url imza doğrulama (PHP). Kayıtlı webhook'tan farkı: bu adresin kendine
// ait bir secret'i YOKTUR; imza anahtarını KENDİ API secret'inizden (sk_...) türetin.
$payload = file_get_contents('php://input');
$sig     = $_SERVER['HTTP_X_POSPAYBU_SIGNATURE'] ?? '';
$keyId   = $_SERVER['HTTP_X_POSPAYBU_KEY'] ?? '';   // hangi pk_ kullanıldı

// X-Pospaybu-Key (pk_...) ile eşleşen API anahtarınızın gizli secret'i:
$apiSecret = 'sk_xxx';

// Türev imza anahtarı — backend ile birebir aynı reçete:
$signingKey = hash_hmac('sha256', 'pospaybu:callback_url:v1', hash('sha256', $apiSecret));

parse_str(str_replace(',', '&', $sig), $p);   // t=..., v1=...
$expected = hash_hmac('sha256', ($p['t'] ?? '') . '.' . $payload, $signingKey);

if (! hash_equals($expected, $p['v1'] ?? '') || abs(time() - (int)($p['t'] ?? 0)) > 300) {
    http_response_code(400); exit('invalid signature');
}
// $event['id'] ile tekilleştirin, sonra 2xx dönün (aksi halde tekrar denenir).
http_response_code(200);

Webhook mü, callback_url mı?

Kayıtlı Webhookcallback_url (S2S)
Payload / zarfAynıAynı
OlaylarHepsi (iade dahil)paid / authorized / failed
İmza secret'iEndpoint'in kendi whsec_ secret'isk_'den türetilen anahtar + X-Pospaybu-Key
KurulumWebhook'lar ekranındanOturum/ödeme isteğinde callback_url

Test Kartları (Sandbox)

POS test modundayken kart numarasının son 4 hanesi sonucu belirler:

AlanTipAçıklama
…0000redKart reddedildi.
…0009hataSağlayıcı hatası → failover (yedek POS).
…00023D3D Secure akışını tetikler.
diğeronayBaşarılı ödeme.

Hatalar & Limitler

  • 401 — kimlik eksik/geçersiz.
  • 402 — banka/kart reddetti (yanıt yine ödeme nesnesidir, status: failed).
  • 422 — doğrulama hatası (alan bazlı).
  • 429 — hız limiti (dakikada 120 istek).
422 örneği
HTTP/1.1 422 Unprocessable Content
{ "message": "The amount field is required.",
  "errors": { "amount": ["The amount field is required."] } }

Tekrarlı POST'larda çift tahsilatı önlemek için Idempotency-Key başlığı gönderin; aynı anahtarla gelen ikinci istek ilk yanıtı tekrar döner (Idempotent-Replayed: true).