Resmi iyzipay SDK'ları imzalama işini sizin için yapar; ama bazen bağımlılık eklemeden, ham REST API'yi anlayarak entegre etmek istersiniz — örneğin paylaşımlı bir hosting'de Composer kullanamadığınızda veya isteğin tam olarak ne gönderdiğini görmek istediğinizde. Bu rehber, SDK'sız, saf PHP + cURL ile yazılmış açık kaynak bir iyzico entegrasyon örneğini baştan sona inceliyor: IYZWSv2 imza şeması, NON-3DS, 3D Secure, Checkout Form ve PWI akışlarının tamamı.
İlgili içerikler: iyzico Node.js entegrasyon rehberi · REST API güvenlik rehberi
Açık Kaynak Örnek Proje
Bu rehberdeki tüm kod örnekleri, github.com/EgemenKEYDAL/Iyzico-Payment-Samples adresindeki açık kaynak projeden alınmıştır — NON-3DS, 3DS, PWI, Checkout Form, BIN sorgulama ve webhook için tamamen Türkçe açıklamalı, kopyala-çalıştır PHP kodları içerir. Projeyi klonlayıp kendi apiKey/secretKey bilgilerinizle doğrudan test edebilirsiniz.
IYZWSv2: Manuel İmza Oluşturma
Resmi SDK kullanmadığınızda, her isteğin Authorization header'ını kendiniz üretmeniz gerekir. iyzico'nun IYZWSv2 şeması şu adımları izler: rastgele bir randomKey üretilir, randomKey + uri + JSON-encode edilmiş istek gövdesi birleştirilip secretKey ile HMAC-SHA256'dan geçirilir, sonuç apiKey:...&randomKey:...&signature:... formatında birleştirilip Base64'e çevrilir.
public function generateAuthorizationHeader($uri, $requestBody = [])
{
$randomKey = (string)(microtime(true) * 10000) . rand(100000, 999999);
$jsonBody = empty($requestBody) ? '' : json_encode($requestBody, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
$dataToEncrypt = $randomKey . $uri . $jsonBody;
$signature = hash_hmac('sha256', $dataToEncrypt, $this->secretKey);
$authString = "apiKey:" . $this->apiKey .
"&randomKey:" . $randomKey .
"&signature:" . $signature;
return base64_encode($authString);
}
// İstek başlığı:
// Authorization: IYZWSv2 <base64-encoded-auth-string>
randomKey her istekte benzersiz olmalıdır (timestamp + rastgele sayı yeterlidir). Aynı randomKey'in tekrar kullanılması veya sabit bir değer olması imza doğrulamasının güvenlik amacını ortadan kaldırır.Dört Ödeme Yöntemi: Hangisini Seçmeli?
| Yöntem | Akış | Ne Zaman Tercih Edilir |
|---|---|---|
| NON-3DS | Tek istekle tamamlanır, 3DS yok | Düşük tutarlı, düşük riskli işlemler |
| 3DS | İki adımlı: initialize → auth, banka OTP'si gerekir | Chargeback riskini azaltmak istediğiniz işlemler |
| Checkout Form | 4 görüntüleme modu (redirect/responsive/popup/iframe), kart verisi sizde değil | Özelleştirilebilir, PCI kapsamını küçültmek isteyenler |
| PWI (iyzico ile Öde) | Kullanıcı iyzico hesabıyla tek tıkla öder, sadece redirect | Alıcı koruma programından faydalanmak isteyenler |
NON-3DS Ödeme
En basit akıştır: kart bilgileri, alıcı ve sepet bilgileriyle birlikte tek bir /payment/auth isteği atılır, yanıt anında gelir.
$paymentRequest = [
'locale' => 'tr',
'conversationId' => 'order-' . uniqid(),
'price' => '100.00',
'paidPrice' => '100.00',
'currency' => 'TRY',
'installment' => 1,
'basketId' => 'B' . time(),
'paymentChannel' => 'WEB',
'paymentGroup' => 'PRODUCT',
'paymentCard' => [
'cardHolderName' => $cardHolderName,
'cardNumber' => $cardNumber,
'expireMonth' => $expireMonth,
'expireYear' => $expireYear,
'cvc' => $cvc,
'registerCard' => 0
],
'buyer' => $buyer,
'shippingAddress' => $shippingAddress,
'billingAddress' => $billingAddress,
'basketItems' => $basketItems
];
$response = $iyzico->sendRequest('/payment/auth', $paymentRequest);
if ($response['status'] == 'success') {
// paymentId'yi kaydet, siparişi onayla
}
3D Secure Akışı: Initialize + Auth
3DS ödemeler iki ayrı isteğe bölünür. Initialize adımı bir threeDSHtmlContent (Base64) döner — bu HTML, kullanıcının bankasından gelen OTP formunu içerir ve doğrudan tarayıcıya basılmalıdır:
// callback.php — banka yönlendirmesi sonrası
$authRequest = [
'locale' => 'tr',
'conversationId' => 'auth-' . uniqid(),
'paymentId' => $_POST['paymentId'],
'conversationData' => $_POST['conversationData'] ?? ''
];
$authResponse = $iyzico->sendRequest('/payment/3dsecure/auth', $authRequest);
if ($authResponse['status'] == 'success' && $authResponse['fraudStatus'] == 1) {
// ödeme kesinleşti
}
mdStatus = 1 başarılı 3DS doğrulamasını gösterir; farklı bir değer banka tarafında doğrulamanın tamamlanmadığı anlamına gelir ve auth isteği başarısız döner.
Checkout Form: 4 Görüntüleme Modu
Checkout Form'u başlattığınızda hem bir token + paymentPageUrl, hem de gömülebilir bir checkoutFormContent (Base64 script) alırsınız. Bunu dört farklı şekilde kullanabilirsiniz:
- Redirect (en yaygın) —
header('Location: ' . $paymentPageUrl); - Responsive — sayfanıza gömülü:
<div id="iyzipay-checkout-form" class="responsive"></div>+ script - Popup — aynı script,
class="popup"ile açılır pencere olarak - iFrame —
<iframe src="{paymentPageUrl}&iframe=true">
Token 30 dakika geçerlidir. Kullanıcı bu süre içinde ödemeyi tamamlamazsa yeniden initialize etmeniz gerekir. Callback'te token ile sonucu sorgularsınız:
$retrieveRequest = [
'locale' => 'tr',
'conversationId' => 'cf-retrieve-' . uniqid(),
'token' => $_POST['token']
];
$result = $iyzico->sendRequest('/payment/iyzipos/checkoutform/auth/ecom/detail', $retrieveRequest);
if ($result['status'] == 'success' && $result['paymentStatus'] == 'SUCCESS' && $result['fraudStatus'] == 1) {
// siparişi onayla
}
Yanıt İmzasının Doğrulanması
İstek imzalamak yeterli değildir — gelen yanıtın da gerçekten iyzico'dan geldiğini doğrulamalısınız. Burada parametre sırası endpoint'e göre değişir; örneğin Checkout Form sonuç sorgulamasında sıra paymentStatus, paymentId, currency, basketId, conversationId, paidPrice, price, token'dır. Parametreler : ile birleştirilip aynı secretKey ile HMAC-SHA256'dan geçirilir ve hash_equals() ile sabit zamanlı karşılaştırılır:
public function verifyResponseSignature($response, $parameterOrder)
{
$params = [];
foreach ($parameterOrder as $param) {
if (isset($response[$param])) {
$value = $response[$param];
if (in_array($param, ['price', 'paidPrice'])) {
$value = rtrim(rtrim((string)$value, '0'), '.'); // trailing zero temizliği
}
$params[] = $value;
}
}
$dataToEncrypt = implode(':', $params);
$calculated = hash_hmac('sha256', $dataToEncrypt, $this->secretKey);
return hash_equals($calculated, $response['signature']);
}
price ve paidPrice alanlarındaki trailing zero'lar (10.50 → 10.5) imza hesabından önce temizlenmelidir; aksi halde doğru bir yanıt için bile imza eşleşmez. Bu en sık karşılaşılan "signature geçersiz" hatasının kaynağıdır.Fraud ve 3DS Zorunluluğu Kontrolü
Bir ödemeyi onayladıktan sonra ürünü göndermeden önce mutlaka fraudStatus alanını kontrol edin; bazı bankalar işlemi anlık onaylasa da iyzico'nun fraud motoru ek inceleme isteyebilir.
| fraudStatus | Anlamı | Aksiyon |
|---|---|---|
| 1 | Onaylandı | Ürünü kargoya verebilirsiniz |
| 0 | İncelemede | Bekleyin, otomatik sonuçlanır |
| -1 | Reddedildi | Ürünü göndermeyin, siparişi iptal edin |
Benzer şekilde, BIN sorgulama (/payment/bin/check) yanıtındaki force3ds alanı kartın 3DS zorunlu olup olmadığını söyler — 1 ise yalnızca 3DS akışıyla ödeme alabilirsiniz, NON-3DS isteği reddedilir.
Webhook: Event Tipleri ve İdempotency
iyzico, ödeme tamamlandığında sunucunuza bir POST bildirimi gönderir; sunucunuz 2xx ile yanıt verene kadar 10 dakikada bir, en fazla 3 kez tekrar dener. Webhook'taki iyziEventType alanı, hangi ödeme yönteminin tetiklediğini söyler:
| iyziEventType | Ödeme Yöntemi |
|---|---|
| API_AUTH | NON-3DS ödeme |
| THREE_DS_AUTH | 3DS ödeme |
| CHECKOUTFORM_AUTH | Checkout Form / PWI ödeme |
| BKM_AUTH | BKM Express ödeme |
Webhook'u callback'le birlikte kullanırken aynı ödemeyi iki kez işlemediğinizden emin olun — paymentId üzerinden veritabanında bir tekillik (unique) kısıtı tanımlamak en güvenli yöntemdir.
Sıkça Sorulan Sorular
SDK kullanmadan entegrasyon yapmak güvenli mi?
Evet, doğru uygulandığında güvenlidir — imzalama mantığı (HMAC-SHA256) açık ve dokümante edilmiştir. Avantajı bağımlılık eklememek ve isteğin tam içeriğini kontrol etmektir; dezavantajı imzalama/doğrulama kodunu siz yazıp test etmeniz gerektiğidir.
"Signature geçersiz" hatası en çok neden alınır?
En sık nedenler: parametre sırasının endpoint'e uymaması, price/paidPrice alanlarında trailing zero temizliğinin yapılmaması, veya JSON body'nin Türkçe karakterler/slash'lar farklı escape edilerek üretilmesi (JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE flag'leri bu yüzden kritiktir).
NON-3DS mi 3DS mi tercih edilmeli?
Mümkünse 3DS tercih edin: chargeback (ters ibraz) riskini bankaya devreder ve PCI sorumluluğunuzu azaltır. Bazı kart tipleri (BIN sorgulamada force3ds=1) zaten yalnızca 3DS ile çalışır.
Kendi ödeme akışınız için PHP, Node.js veya başka bir stack'te profesyonel iyzico entegrasyonu desteği alın. Teklif Alın