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.
Ödeme Servisleri — hangi servisle tahsilat yapabilirim?
Tahsilata başlamadan önce bu ucu çağırın: hesabınızda kullanılabilir ödeme servislerini (sanal POS) döner. Dönen id'yi ödeme isteğinde virtual_pos_id olarak göndererek servisi açıkça seçersiniz. Aynı liste hem iframe / Hosted Checkout hem de Direkt API akışında geçerlidir.
/v1/payment-services?currency=&amount=&installment_count=Filtreler (hepsi opsiyonel)
| Alan | Tip | Açıklama |
|---|---|---|
| currency | string | ISO-4217, 3 harf (TRY, USD…). Yalnız bu para birimini kabul eden servisler döner. |
| amount | number | Tahsil edilecek tutar. Aylık tahsilat limiti bu tutarla dolacak servisler listeden elenir. |
| installment_count | int | 1–36 arası taksit sayısı (1 = tek çekim). Bu taksidi destekleyen servisler döner. |
curl "https://pospaybu.com/api/v1/payment-services?currency=TRY&amount=1000&installment_count=3" \
-H "X-Api-Key: pk_xxx" -H "X-Api-Secret: sk_xxx"
// Yanıt:
{ "data": [
{
"id": 12,
"type": "virtual_pos",
"name": "Garanti TRY",
"provider": "garanti",
"bank_code": "0062",
"mode": "live", // live | test
"currency": "TRY", // null = sağlayıcının desteklediği hepsi
"supported_currencies": ["TRY", "USD"],
"max_installment": 12,
"supports_3d": true,
"checkout_mode": "hosted", // hosted | direct
"condition_group": { "id": 3, "name": "Kampanya", "slug": "kampanya" }
}
] }Yanıt alanları
| Alan | Tip | Açıklama |
|---|---|---|
| id | int | Servisin kimliği. Ödeme isteğinde virtual_pos_id olarak bunu gönderin. |
| type | string | Servis tipi. Bugün yalnız virtual_pos döner; ileride eklenecek tipler eski entegrasyonunuzu kırmaz. |
| name | string | Panelde bu servise verdiğiniz ad. |
| provider | string | Sağlayıcı kodu (garanti, craftgate, …). |
| bank_code | string | Bankanın EFT kodu (tanımlıysa). |
| mode | string | live | test — servisin çalıştığı mod. |
| currency | string | null | Servise sabitlenmiş para birimi; null = sağlayıcının desteklediği tüm birimler. |
| supported_currencies | string[] | Sağlayıcı protokolünün kabul ettiği para birimleri; boş dizi = kısıt bilinmiyor. |
| max_installment | int | Bu servisle yapılabilecek en yüksek taksit sayısı. |
| supports_3d | bool | 3D Secure desteği. |
| checkout_mode | string | hosted → müşteri sağlayıcının kendi ödeme sayfasına yönlendirilir. direct → kart bilgisi bizim akışımızda toplanır. |
| condition_group | object | null | Bu servis seçildiğinde (istekte açık bir grup yoksa) uygulanacak ödeme koşulu grubu: id, name, slug. |
Yanıt yalnızca seçim için gereken yapılandırmayı içerir; hiçbir kredensiyel, secret ya da token döndürülmez.
Servisi seçme
# 1) Hosted / iframe — oturumu bu servise SABİTLE
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", "virtual_pos_id": 12 }'
# 2) Direkt API — bu tahsilatı bu servisle al
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": 149.90, "virtual_pos_id": 12, "card": { ... } }'
# virtual_pos_id göndermezseniz davranış eskisi gibidir: POS'u
# biz otomatik yönlendiririz.Listede olmayan, pasif ya da başka bir hesaba ait bir virtual_pos_id gönderirseniz istek 422 ile reddedilir (virtual_pos_id alan hatası). Kimlik eksik/geçersizse 401, filtreler geçersizse yine 422 döner.
Hangi servisler listelenir?
Yalnızca aktif ve panelde API'de görünsün işaretli servisler. Sanal POS'lar ekranından bir POS'u API'de gizlerseniz burada dönmez ve virtual_pos_id ile seçilemez; buna karşılık panelin ve otomatik POS yönlendirmesinin davranışı değişmez — gizlenmiş POS otomatik yönlendirmeye dahil olmaya devam eder.
Seçtiğiniz servis sonradan kapanırsa: Tahsilat anında seçilen servis kullanılamaz hale gelmişse (örn. panelden pasife alındıysa) ödeme reddedilir — sessizce başka bir servise düşülmez. Böylece para, satıcının seçmediği bir servise gitmez. Müşteriye anlaşılır bir mesaj döner; devam etmek için listeyi yeniden çekip yeni bir virtual_pos_id ile ödemeyi başlatın.
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 }
}'
# ── Opsiyonel: ödeme servisini (sanal POS) kendiniz seçin ──────
# Gövdeye şu alanı ekleyin, oturum o servise sabitlenir:
# "virtual_pos_id": 12
#
# Geçerli id'leri GET /v1/payment-services döner; buradaki 12 yalnızca
# örnektir, kendi hesabınızdaki id'yi kullanın (listede olmayan id → 422).
# Göndermezseniz POS'u biz otomatik yönlendiririz.HTTP/1.1 201 Created
{
"token": "Hh3k...long-unguessable-token",
"reference": "SIPARIS-1001",
"checkout_url": "https://pospaybu.com/checkout/Hh3k...long-unguessable-token",
// Oturuma sabitlenen ödeme servisi; null = otomatik POS yönlendirmesi.
"virtual_pos_id": null,
// Uygulanan koşul grubu; grup gönderilmediyse ve varsayılan yoksa null.
"condition_group": { "id": 7, "name": "Kampanya", "slug": "kampanya" }
}Ödeme servisi seçimi (opsiyonel)
Opsiyonel virtual_pos_id gönderirseniz oturum o ödeme servisine sabitlenir: oturumun tüm tahsilat akışları (kartla ödeme, hosted yönlendirme, parçalı/split ödemeler) o servisle çalışır. Geçerli id'leri Ödeme Servisleri ucundan alırsınız; listede olmayan bir id 422 döner. Göndermezseniz davranış eskisi gibidir (otomatik yönlendirme) ve yanıttaki virtual_pos_id null gelir.
Ö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). |
| virtual_pos_id | int | Ödeme servisini açıkça seçin (opsiyonel). Yalnız /v1/payment-services listesinde dönen id'ler kabul edilir; listede olmayan / pasif id → 422. |
| metadata | object | İstediğiniz ek alanlar. |
Belirli bir servisle tahsilat almak isterseniz virtual_pos_id gönderin; geçerli id'leri Ödeme Servisleri ucundan alırsınız. Göndermezseniz POS yönlendirmesi eskisi gibi otomatiktir.
İ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 |
|---|---|---|
| GET | /v1/payment-services | Ödeme servislerini (sanal POS) listele |
| 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).