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>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:
$initResponse = $iyzico->sendRequest('/payment/3dsecure/initialize', $initializeRequest);
// HTML'i tarayıcıya bas
echo base64_decode($initResponse['threeDSHtmlContent']);
// Kullanıcı OTP'yi girip onayladıktan sonra banka,
// callbackUrl'inize POST yapar (paymentId, mdStatus, conversationId ile)// 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']);
}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.