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ış
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)
Kullanım: anında teslim edebildiğinizde (dijital ürün, abonelik vb.).
Provizyon + Kapama akışı (iki adım)
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.
/v1/checkout/sessionscurl -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 }
}'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.
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
<!-- Ö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.
/v1/payments| Alan | Tip | Açıklama |
|---|---|---|
| amount | number | Tahsil edilecek tutar (zorunlu). |
| currency | string | ISO-4217, varsayılan TRY. |
| installment_count | int | Taksit sayısı (1 = tek çekim). |
| pre_auth | bool | true → provizyon (blokede tut, sonra kapat). |
| card | object | number, holder, exp_month, exp_year, cvv (zorunlu). |
| customer | object | name, email, phone, code (cari kodu), ip + ayarladığınız özel alanlar. code/email ile cari kaydına bağlanır. |
| callback_url | url | 3D sonrası müşterinin döneceği adres. |
| reference | string | Sipariş numaranız (verilmezse üretilir). |
| metadata | object | İ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 }
}'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ışı
// 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.
/v1/installments?amount=&card_brand=&virtual_pos_id=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).
/v1/payments/{reference}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.
/v1/payments/{reference}/refund# 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.
# 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.
/v1/meal-payments/balance/v1/meal-payments# 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
| Method | Endpoint | Açıklama |
|---|---|---|
| POST | /v1/payments | Kart ile ödeme (Direkt / S2S) |
| POST | /v1/checkout/sessions | Hosted / iframe oturumu oluştur |
| GET | /v1/installments | Taksit seçeneklerini sorgula |
| GET | /v1/payments/{reference} | Ödeme durumunu sorgula |
| POST | /v1/payments/{reference}/refund | İade (tam / kısmi) |
| POST | /v1/meal-payments/balance | Yemek kartı bakiye sorgu |
| POST | /v1/meal-payments | Yemek 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 edildipayment.failed— başarısızpayment.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 }.
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):
"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
// İ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.
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
// 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ı Webhook | callback_url (S2S) | |
|---|---|---|
| Payload / zarf | Aynı | Aynı |
| Olaylar | Hepsi (iade dahil) | paid / authorized / failed |
| İmza secret'i | Endpoint'in kendi whsec_ secret'i | sk_'den türetilen anahtar + X-Pospaybu-Key |
| Kurulum | Webhook'lar ekranından | Oturum/ödeme isteğinde callback_url |
Test Kartları (Sandbox)
POS test modundayken kart numarasının son 4 hanesi sonucu belirler:
| Alan | Tip | Açıklama |
|---|---|---|
| …0000 | red | Kart reddedildi. |
| …0009 | hata | Sağlayıcı hatası → failover (yedek POS). |
| …0002 | 3D | 3D Secure akışını tetikler. |
| diğer | onay | Baş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).
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).