Next.js ile İyzico Entegrasyonu: Checkout Form ve 3D Secure Rehberi
iyzipay npm paketi kurulumu, TypeScript tip tanımları, Checkout Form akışı, 3D Secure callback, webhook IPN doğrulama ve sub-merchant. Next.js 16 App Router ile tam entegrasyon rehberi.

Önemli Not: Bu yazıdaki teknik bilgiler yazım tarihi itibarıyla geçerlidir. Kullanılan kütüphaneler, API'ler ve servisler zaman içinde değişebilir. Ücretlendirme, yasal düzenleme ve vergi konularında ilgili resmi kaynakları ve uzmanları referans alınız. Bu içerik bilgilendirme amaçlı olup herhangi bir finansal veya hukuki tavsiye niteliği taşımamaktadır.
İyzico, Türkiye'nin en yüksek işlem hacmine sahip ödeme altyapılarından biri. Sahibinden, Decathlon ve LetGo gibi büyük platformların güvendiği bu sistem, 1 Nisan 2025'te PayU tarafından satın alındı — 165 milyon dolarlık bu anlaşma Türkiye fintech tarihinin en büyük edinimleri arasında yer alıyor. Marka adı "iyzico" olarak devam ediyor.
Bu rehberde Next.js 16 App Router ile iyzico'yu adım adım entegre edeceğiz. Checkout Form (iframe) akışı, 3D Secure callback yönetimi, webhook (IPN) imza doğrulama ve sub-merchant kullanımını TypeScript ile ele alacağız.
Komisyon oranları ve güncel fiyatlandırma için iyzico'nun resmi sayfasını ziyaret edin — bu bilgiler değişkenlik gösterebilir ve burada yer verilmemiştir.
1. Ön Koşullar ve Sandbox Hesabı
Başlamadan önce şunlara ihtiyacınız var:
- Node.js 18+ veya Bun (bu rehberde Bun kullanıyoruz)
- Next.js 16 App Router projesi
- İyzico sandbox hesabı
Sandbox hesabı açma:
- sandbox.iyzico.com adresine gidin
- Üye iş yeri kaydı oluşturun
- Dashboard'dan API Key ve Secret Key alın
# Sandbox endpoint'leri
API: https://sandbox-api.iyzipay.com
Panel: https://sandbox.iyzico.com
Production'a geçişte sadece uri parametresini https://api.iyzipay.com olarak değiştirmeniz yeterli.
2. iyzipay npm Paketi ve TypeScript Tip Sorunu
iyzico'nun resmi npm paketi iyzipay, Şubat 2026 itibarıyla v2.0.65 sürümündedir ve %100 JavaScript ile yazılmıştır — TypeScript tip tanımları içermez.
bun add iyzipay
Paketi kurar kurmaz TypeScript projenizde şu hatayı görürsünüz:
Could not find a declaration file for module 'iyzipay'.
@types/iyzipay de npm'de mevcut değil. Çözüm: kendi tip tanımlarınızı yazmak.
2.1 Tip Tanımları Oluşturma
Proje kökünde types/iyzipay.d.ts dosyası oluşturun:
// types/iyzipay.d.ts
declare module "iyzipay" {
interface IyzicoConfig {
apiKey: string
secretKey: string
uri: string
}
interface Buyer {
id: string
name: string
surname: string
gsmNumber: string
email: string
identityNumber: string
lastLoginDate?: string
registrationDate?: string
registrationAddress: string
ip: string
city: string
country: string
zipCode?: string
}
interface Address {
contactName: string
city: string
country: string
address: string
zipCode?: string
}
interface BasketItem {
id: string
name: string
category1: string
category2?: string
itemType: "PHYSICAL" | "VIRTUAL"
price: string
subMerchantKey?: string
subMerchantPrice?: string
}
interface CheckoutFormInitializeRequest {
locale?: string
conversationId?: string
price: string
paidPrice: string
currency: string
basketId: string
paymentGroup: "PRODUCT" | "LISTING" | "SUBSCRIPTION"
callbackUrl: string
enabledInstallments?: number[]
buyer: Buyer
shippingAddress: Address
billingAddress: Address
basketItems: BasketItem[]
debitCardAllowed?: boolean
}
interface CheckoutFormRetrieveRequest {
locale?: string
conversationId?: string
token: string
}
interface IyzicoResponse {
status: "success" | "failure"
errorCode?: string
errorMessage?: string
errorGroup?: string
locale?: string
systemTime?: number
conversationId?: string
}
interface CheckoutFormInitializeResponse extends IyzicoResponse {
checkoutFormContent?: string
tokenExpireTime?: number
paymentPageUrl?: string
token?: string
}
interface CheckoutFormRetrieveResponse extends IyzicoResponse {
paymentStatus?: string
paymentId?: string
fraudStatus?: number
merchantCommissionRate?: number
merchantCommissionRateAmount?: number
iyziCommissionRateAmount?: number
iyziCommissionFee?: number
cardType?: string
cardAssociation?: string
cardFamily?: string
binNumber?: string
basketId?: string
currency?: string
itemTransactions?: ItemTransaction[]
}
interface ItemTransaction {
itemId?: string
paymentTransactionId?: string
transactionStatus?: number
price?: string
paidPrice?: string
merchantCommissionRate?: number
merchantCommissionRateAmount?: number
iyziCommissionRateAmount?: number
iyziCommissionFee?: number
blockageRate?: number
blockageRateAmountMerchant?: number
blockageRateAmountSubMerchant?: number
blockageResolvedDate?: string
subMerchantKey?: string
subMerchantPrice?: string
subMerchantPayoutRate?: number
subMerchantPayoutAmount?: number
merchantPayoutAmount?: number
}
class Iyzipay {
constructor(config: IyzicoConfig)
checkoutFormInitialize: {
create(
request: CheckoutFormInitializeRequest,
callback: (err: Error | null, result: CheckoutFormInitializeResponse) => void
): void
}
checkoutFormResult: {
retrieve(
request: CheckoutFormRetrieveRequest,
callback: (err: Error | null, result: CheckoutFormRetrieveResponse) => void
): void
}
}
export = Iyzipay
}
Bu tip tanımları temel akış için yeterli. Sub-merchant veya taksit API'si kullanırsanız ilgili tipleri ekleyebilirsiniz.
2.2 İyzico Client'ı Başlatma
// lib/iyzico.ts
import Iyzipay from "iyzipay"
import { env } from "@/lib/env"
export const iyzico = new Iyzipay({
apiKey: env.IYZICO_API_KEY,
secretKey: env.IYZICO_SECRET_KEY,
uri: env.IYZICO_BASE_URL, // sandbox veya production
})
# .env.local
IYZICO_API_KEY=sandbox-...
IYZICO_SECRET_KEY=sandbox-...
IYZICO_BASE_URL=https://sandbox-api.iyzipay.com
Üretim ortamında
IYZICO_BASE_URLdeğerinihttps://api.iyzipay.comolarak ayarlayın.
3. Checkout Form (CF) Akışı
İyzico'nun Checkout Form (iframe) yöntemi önerilen entegrasyon şeklidir. Kart bilgileri doğrudan iyzico'nun güvenli alanında işlenir; PCI uyumluluğu otomatik sağlanır.
Akış iki adımdan oluşur:
- CF-Initialize: Sunucu tarafında checkout form token'ı oluştur
- CF-Retrieve: Ödeme tamamlandıktan sonra sonucu çek
3.1 CF-Initialize: Token Oluşturma
// app/api/iyzico/checkout/route.ts
import { type NextRequest, NextResponse } from "next/server"
import { iyzico } from "@/lib/iyzico"
import { env } from "@/lib/env"
// Promise wrapper — iyzipay callback tabanlı çalışır
function initializeCheckoutForm(request: Parameters<typeof iyzico.checkoutFormInitialize.create>[0]) {
return new Promise<ReturnType<Parameters<typeof iyzico.checkoutFormInitialize.create>[1]> extends (err: any, result: infer R) => void ? R : never>((resolve, reject) => {
iyzico.checkoutFormInitialize.create(request, (err, result) => {
if (err) reject(err)
else resolve(result as any)
})
})
}
export async function POST(req: NextRequest) {
try {
const body = await req.json()
const { orderId, amount, customerEmail, basketItems } = body
// Müşteri IP'sini al
const ip =
req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ??
req.headers.get("x-real-ip") ??
"85.34.78.112" // fallback (sandbox için)
const request = {
locale: "tr",
conversationId: orderId,
price: amount.toString(),
paidPrice: amount.toString(),
currency: "TRY",
basketId: `BASKET_${orderId}`,
paymentGroup: "PRODUCT" as const,
callbackUrl: `${env.NEXT_PUBLIC_BASE_URL}/odeme/callback`,
enabledInstallments: [2, 3, 6, 9, 12],
buyer: {
id: `BUYER_${orderId}`,
name: body.firstName,
surname: body.lastName,
gsmNumber: body.phone,
email: customerEmail,
identityNumber: "74300864791", // TC Kimlik No (sandbox için sabit)
registrationAddress: body.address,
ip,
city: body.city,
country: "Turkey",
zipCode: body.zipCode ?? "34000",
},
shippingAddress: {
contactName: `${body.firstName} ${body.lastName}`,
city: body.city,
country: "Turkey",
address: body.address,
},
billingAddress: {
contactName: `${body.firstName} ${body.lastName}`,
city: body.city,
country: "Turkey",
address: body.address,
},
basketItems: basketItems.map((item: { id: string; name: string; price: number; type?: string }) => ({
id: item.id,
name: item.name,
category1: "Genel",
itemType: (item.type === "digital" ? "VIRTUAL" : "PHYSICAL") as "PHYSICAL" | "VIRTUAL",
price: item.price.toString(),
})),
}
const result = await initializeCheckoutForm(request)
if (result.status !== "success") {
return NextResponse.json(
{ error: result.errorMessage ?? "Ödeme başlatılamadı" },
{ status: 400 }
)
}
return NextResponse.json({
checkoutFormContent: result.checkoutFormContent,
token: result.token,
tokenExpireTime: result.tokenExpireTime,
})
} catch (error) {
console.error("İyzico CF Initialize error:", error)
return NextResponse.json({ error: "Sunucu hatası" }, { status: 500 })
}
}
3.2 Checkout Form Bileşeni (Client-Side)
// components/IyzicoCheckoutForm.tsx
"use client"
import { useEffect, useRef } from "react"
interface IyzicoCheckoutFormProps {
checkoutFormContent: string
}
export function IyzicoCheckoutForm({ checkoutFormContent }: IyzicoCheckoutFormProps) {
const containerRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!containerRef.current || !checkoutFormContent) return
// iyzico'nun checkout form içeriğini DOM'a ekle
containerRef.current.innerHTML = checkoutFormContent
// Script tag'lerini çalıştır (innerHTML ile eklenen script'ler çalışmaz)
const scripts = containerRef.current.querySelectorAll("script")
scripts.forEach((oldScript) => {
const newScript = document.createElement("script")
Array.from(oldScript.attributes).forEach((attr) => {
newScript.setAttribute(attr.name, attr.value)
})
newScript.textContent = oldScript.textContent
oldScript.parentNode?.replaceChild(newScript, oldScript)
})
}, [checkoutFormContent])
return (
<div
ref={containerRef}
className="min-h-[400px] w-full"
id="iyzipay-checkout-form"
/>
)
}
3.3 Ödeme Sayfası (Server Component)
// app/odeme/page.tsx
import { IyzicoCheckoutForm } from "@/components/IyzicoCheckoutForm"
async function getCheckoutForm(orderId: string) {
const res = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/iyzico/checkout`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
orderId,
amount: "150.00",
customerEmail: "test@example.com",
firstName: "Ahmet",
lastName: "Yılmaz",
phone: "+905350000000",
address: "Nispetiye Cad. No:1",
city: "Istanbul",
basketItems: [{ id: "URUN_001", name: "Ürün Adı", price: 150 }],
}),
cache: "no-store",
})
return res.json()
}
export default async function OdemePage() {
const { checkoutFormContent, error } = await getCheckoutForm("ORDER_123")
if (error) {
return <div className="text-red-500">Hata: {error}</div>
}
return (
<div className="mx-auto max-w-2xl px-4 py-8">
<h1 className="mb-6 text-2xl font-bold">Güvenli Ödeme</h1>
<IyzicoCheckoutForm checkoutFormContent={checkoutFormContent} />
</div>
)
}
4. CF-Retrieve: Ödeme Sonucunu Çekme
Ödeme tamamlandığında iyzico, callbackUrl'inize bir POST isteği gönderir. Bu isteğin body'sinde token bulunur. Bu token ile ödeme sonucunu sorgulamanız gerekir.
// app/odeme/callback/route.ts
import { type NextRequest, NextResponse } from "next/server"
import { iyzico } from "@/lib/iyzico"
function retrieveCheckoutForm(token: string) {
return new Promise<any>((resolve, reject) => {
iyzico.checkoutFormResult.retrieve(
{ locale: "tr", token },
(err, result) => {
if (err) reject(err)
else resolve(result)
}
)
})
}
export async function POST(req: NextRequest) {
try {
const formData = await req.formData()
const token = formData.get("token") as string
if (!token) {
return NextResponse.redirect(new URL("/odeme/hata?reason=no-token", req.url))
}
const result = await retrieveCheckoutForm(token)
if (result.status !== "success" || result.paymentStatus !== "SUCCESS") {
console.error("Ödeme başarısız:", result.errorMessage)
return NextResponse.redirect(
new URL(`/odeme/hata?reason=${result.errorCode ?? "unknown"}`, req.url)
)
}
// Ödeme başarılı — veritabanında siparişi güncelle
const orderId = result.conversationId
await updateOrderStatus(orderId, "PAID", result.paymentId)
return NextResponse.redirect(new URL(`/odeme/basarili?order=${orderId}`, req.url))
} catch (error) {
console.error("CF Retrieve error:", error)
return NextResponse.redirect(new URL("/odeme/hata?reason=server-error", req.url))
}
}
async function updateOrderStatus(orderId: string, status: string, paymentId?: string) {
// Drizzle ORM ile veritabanı güncelleme — kendi implementasyonunuza göre uyarlayın
console.log(`Sipariş güncellendi: ${orderId} → ${status} (paymentId: ${paymentId})`)
}
Önemli: iyzico callback'i bir form POST olarak gönderir, JSON değil. Bu yüzden
req.formData()kullanıyoruz.
5. 3D Secure Akışı (Direct API)
Checkout Form yöntemi 3DS'i otomatik yönetir. Ancak kart bilgilerini kendiniz topladığınız direkt API yöntemini kullanıyorsanız 3DS akışını manuel olarak yönetmeniz gerekir.
Türkiye'de tüm banka kartları için 3DS pratikte zorunludur — tüm bankalar SMS OTP (3DS v1) uygular.
5.1 3DS Akışı — 6 Adım
- BIN Kontrolü: Kart tipini ve 3DS zorunluluğunu sorgula
- 3DS Başlat: Kart + alıcı bilgileri +
callbackUrlile POST - HTML Decode: Yanıttaki Base64 HTML içeriğini decode edip kullanıcıya render et
- OTP Girişi: Kullanıcı SMS kodunu girer → banka otomatik
callbackUrl'e yönlendirir - 3DS Doğrula: Callback'ten gelen
paymentIdile sonucu onayla - Webhook Bildirimi: IPN sistemi ek bildirim gönderir
Callback URL'den gelen alanlar:
status — success / failure
paymentId — iyzico ödeme ID'si
mdStatus — 1: başarılı 3DS, 0-8: çeşitli hatalar
conversationId — kendi sipariş ID'niz
conversationData — ek veri
mdStatus değerlerini yorumlamak kritik:
| mdStatus | Açıklama |
|---|---|
| 1 | Tam 3DS doğrulama — devam et |
| 2, 3, 4 | Kısmi doğrulama — riske göre karar ver |
| 0, 5-8 | Başarısız — ödemeyi reddet |
6. Webhook (IPN) Doğrulama
İyzico, ödeme olaylarını webhook ile bildirir. Header: X-IYZ-SIGNATURE-V3
Not: Eski
X-IYZ-SIGNATUREheader'ı deprecated.V3kullanın.
6.1 İmza Doğrulama
// app/api/iyzico/webhook/route.ts
import { type NextRequest, NextResponse } from "next/server"
import { createHmac } from "node:crypto"
import { env } from "@/lib/env"
function verifyIyzicoSignature(
secretKey: string,
payload: Record<string, string>,
receivedSignature: string
): boolean {
// Direct API için alan sırası:
// secretKey + iyziEventType + paymentId + paymentConversationId + status
const dataToSign = [
secretKey,
payload.iyziEventType,
payload.paymentId,
payload.paymentConversationId,
payload.status,
]
.filter(Boolean)
.join("")
const expectedSignature = createHmac("sha256", secretKey)
.update(dataToSign)
.digest("hex")
// Timing-safe karşılaştırma
const receivedBuffer = Buffer.from(receivedSignature, "hex")
const expectedBuffer = Buffer.from(expectedSignature, "hex")
if (receivedBuffer.length !== expectedBuffer.length) return false
return (
Buffer.compare(
Buffer.from(createHmac("sha256", secretKey).update(receivedSignature).digest("hex"), "hex"),
Buffer.from(createHmac("sha256", secretKey).update(expectedSignature).digest("hex"), "hex")
) === 0
)
}
export async function POST(req: NextRequest) {
const signature = req.headers.get("x-iyz-signature-v3")
if (!signature) {
return NextResponse.json({ error: "Signature eksik" }, { status: 401 })
}
const body = await req.json()
const isValid = verifyIyzicoSignature(env.IYZICO_SECRET_KEY, body, signature)
if (!isValid) {
console.error("Geçersiz iyzico webhook imzası")
return NextResponse.json({ error: "Geçersiz imza" }, { status: 401 })
}
const { iyziEventType, paymentId, status } = body
switch (iyziEventType) {
case "CHECKOUT_FORM_AUTH":
case "PAYMENT_API":
if (status === "SUCCESS") {
await handleSuccessfulPayment(paymentId)
} else {
await handleFailedPayment(paymentId)
}
break
case "THREE_DS_AUTH":
case "THREE_DS_CALLBACK":
// 3DS olayları — gerekirse işle
break
default:
console.log(`Bilinmeyen iyzico event: ${iyziEventType}`)
}
// iyzico 200 bekler; aksi hâlde 10 dakikada bir, max 3 kez yeniden dener
return NextResponse.json({ status: "ok" })
}
async function handleSuccessfulPayment(paymentId: string) {
console.log(`Başarılı ödeme işlendi: ${paymentId}`)
}
async function handleFailedPayment(paymentId: string) {
console.log(`Başarısız ödeme kaydedildi: ${paymentId}`)
}
Yeniden deneme politikası: İyzico webhook'u başarısız olursa 10 dakikada bir tekrar dener, maksimum 3 deneme. Endpoint'iniz 200 dönmezse bu süreç tetiklenir.
Olay tipleri: PAYMENT_API, API_AUTH, THREE_DS_AUTH, THREE_DS_CALLBACK, CHECKOUT_FORM_AUTH, BANK_TRANSFER_AUTH
7. Sub-Merchant (Marketplace Bölünmüş Ödeme)
Birden fazla satıcının yer aldığı marketplace projelerinde sub-merchant özelliğini kullanabilirsiniz.
7.1 Sub-Merchant Oluşturma
// app/api/iyzico/submerchant/route.ts
import { type NextRequest, NextResponse } from "next/server"
export async function POST(req: NextRequest) {
const { name, email, address, taxNumber, taxOffice, legalCompanyTitle } = await req.json()
// İyzico sub-merchant API'si henüz resmi SDK tiplerinde yok
// Doğrudan HTTP isteği kullanıyoruz
const payload = {
locale: "tr",
conversationId: `SM_${Date.now()}`,
subMerchantExternalId: `MERCHANT_${Date.now()}`,
subMerchantType: "LIMITED_OR_JOINT_STOCK_COMPANY", // PERSONAL | PRIVATE_COMPANY | LIMITED_OR_JOINT_STOCK_COMPANY
address,
taxOffice,
taxNumber,
legalCompanyTitle,
email,
iban: "TR630006200119000006672315", // Sandbox IBAN
currency: "TRY",
}
const authHeader = Buffer.from(
`${process.env.IYZICO_API_KEY}:${process.env.IYZICO_SECRET_KEY}`
).toString("base64")
const response = await fetch(`${process.env.IYZICO_BASE_URL}/onboarding/submerchant`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Basic ${authHeader}`,
},
body: JSON.stringify(payload),
})
const result = await response.json()
return NextResponse.json(result)
}
7.2 Ödeme Sırasında Sub-Merchant Kullanımı
Basket item'larında subMerchantKey ve subMerchantPrice ekleyin:
basketItems: [
{
id: "URUN_001",
name: "Satıcı A - Ürün",
category1: "Elektronik",
itemType: "PHYSICAL",
price: "80.00",
subMerchantKey: "sub-merchant-key-satici-a", // onboarding'den dönen key
subMerchantPrice: "72.00", // satıcıya ödenecek tutar (komisyon düşülmüş)
},
{
id: "URUN_002",
name: "Satıcı B - Ürün",
category1: "Giyim",
itemType: "PHYSICAL",
price: "70.00",
subMerchantKey: "sub-merchant-key-satici-b",
subMerchantPrice: "63.00",
},
],
8. Test Kartları
Sandbox ortamında şu kartları kullanın:
| Kart No | Tür | Senaryo |
|---|---|---|
| 4766620000000001 | Visa | Başarılı ödeme |
| 5528790000000008 | MasterCard | Başarılı ödeme |
| 374427000000003 | AmEx | Başarılı ödeme |
| 4111111111111129 | — | Yetersiz bakiye |
| 4125111111111115 | — | Süresi dolmuş kart |
Tüm test kartları için son kullanma tarihi olarak gelecekte bir ay/yıl, CVV olarak 123 kullanabilirsiniz. 3DS testinde sandbox otomatik olarak başarı/hata senaryolarını simüle eder.
9. Ortam Değişkenleri ve Tip Güvenliği
lib/env.ts'e iyzico değişkenlerini ekleyin:
// lib/env.ts (ilgili kısım)
import { z } from "zod/v4"
const envSchema = z.object({
// ... mevcut değişkenler
// İyzico
IYZICO_API_KEY: z.string().min(1),
IYZICO_SECRET_KEY: z.string().min(1),
IYZICO_BASE_URL: z.string().url().default("https://sandbox-api.iyzipay.com"),
})
10. Sık Hatalar ve Çözümleri
Hata: "conversationId is already used"
Her ödeme girişimi için benzersiz bir conversationId kullanın. Aynı ID ile ikinci deneme yapılırsa bu hatayı alırsınız.
conversationId: `ORDER_${orderId}_${Date.now()}`
Hata: Callback URL'ye POST gelmiyor
İyzico callback URL'sine erişebilmesi için yerel geliştirmede ngrok veya benzeri bir tünel kullanın:
ngrok http 3000
# Çıkan URL'yi callbackUrl olarak kullanın:
# https://abc123.ngrok.io/odeme/callback
Hata: "basketItemPrice must be equal to price"
basketItems içindeki tüm price değerlerinin toplamı, form üst seviyedeki price değerine tam olarak eşit olmalıdır. Ondalık hassasiyetine dikkat edin.
// Yanlış: kayan nokta hatası riski
const total = items.reduce((sum, item) => sum + item.price, 0)
// Doğru: Tamsayı üzerinden hesapla, sonra string'e çevir
const totalKurus = items.reduce((sum, item) => sum + Math.round(item.price * 100), 0)
const total = (totalKurus / 100).toFixed(2)
Hata: TypeScript tip hataları
types/iyzipay.d.ts dosyasının tsconfig.json'daki include listesinde olduğundan emin olun:
{
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "types/**/*.d.ts"]
}
Hata: "token is expired"
tokenExpireTime süresi dolmadan kullanıcı formu tamamlamazsa token geçersiz hâle gelir. Kullanıcıya yeni bir token oluşturma seçeneği sunun.
Webhook Doğrulaması Başarısız Oluyor
İmza doğrulamasında alan sırası kritik. Direct API ve Checkout Form için imza hesaplamaları farklıdır. Resmi iyzico dokümantasyonundan kullandığınız entegrasyon tipine uygun sırayı kontrol edin.
11. KVKK ve Güvenlik Notları
- Kart bilgileri hiçbir zaman kendi sunucunuzdan geçmez; Checkout Form yönteminde kart verileri doğrudan iyzico'ya gönderilir
- Buyer
identityNumber(TC Kimlik No) kişisel veri olarak işlenir — KVKK kapsamında veri işleme politikanızı güncellemeniz gerekebilir - IP adresi iyzico'ya gönderilir; aydınlatma metninde belirtin
- Production'da
secretKeyveapiKeyyalnızca sunucu tarafında kullanılmalı, client-side bundle'a kesinlikle dahil edilmemeli
12. Production Geçiş Kontrol Listesi
Canlıya geçmeden önce şunları tamamlayın:
-
IYZICO_BASE_URL'ihttps://api.iyzipay.comolarak güncelle - Sandbox
apiKey/secretKeyyerine production kimlik bilgilerini gir - Callback URL'lerin production domain'ine işaret ettiğini doğrula
- Webhook endpoint'inin gerçek imzaları doğruladığını test et
- Test siparişi ile uçtan uca ödeme akışını doğrula
- Başarısız ödeme ve hata durumlarını test et
-
conversationIdbenzersizliğini garanti altına al (UUID veya timestamp ile) - Ödeme loglarını güvenli şekilde sakla (kart numarası asla loglanmamalı)
- İyzico merchant panelinizde webhook URL'ini production endpoint olarak ayarla
- Rate limiting ve hata yönetimi endpoint'lerine uygulanmış olmalı
Sıkça Sorulan Sorular
iyzipay paketi yerine fetch ile direkt API kullanabilir miyim?
Evet. iyzipay paketi temel olarak HMAC imzalama ve HTTP isteklerini yönetir. Kendi HTTP client'ınızı yazabilirsiniz. Ancak imzalama mantığını doğru uygulamak kritiktir; bu nedenle resmi SDK genellikle tercih edilir.
Checkout Form ile 3DS akışı arasındaki fark nedir?
Checkout Form (iframe) yönteminde iyzico 3DS'i otomatik yönetir — siz müdahale etmezsiniz. Direkt API yönteminde ise 3DS adımlarını kendiniz orkestrasyonlarsınız. Çoğu proje için Checkout Form yeterledir.
price ve paidPrice arasındaki fark ne?
price ürünlerin gerçek toplam fiyatı, paidPrice ise müşterinin ödeyeceği nihai tutardır. Taksit faizi veya indirim uygulandığında bu iki değer farklılaşabilir.
Yerel geliştirmede callback nasıl test edilir?
ngrok http 3000 komutuyla tünel açın ve çıkan URL'yi callbackUrl olarak kullanın. Vercel önizleme URL'leri de bu amaçla kullanılabilir.
iyzico'nun PayU tarafından satın alınması entegrasyonu etkiler mi?
Teknik API ve SDK açısından şu an için herhangi bir değişiklik yok. Marka "iyzico" olarak devam ediyor. Gelecekteki değişiklikler için resmi iyzico duyurularını takip edin.
Komisyon oranları hakkında bilgi nereden alırım?
Güncel komisyon oranları, sektöre ve işlem hacminize göre belirlenir ve değişkenlik gösterir. Detaylar için iyzico.com üzerinden iyzico ile doğrudan iletişime geçin.
Sonuç
Next.js 16 App Router ile iyzico entegrasyonu birkaç temel bileşenden oluşuyor: TypeScript tip tanımları (SDK'da eksik olduğu için kendiniz yazmanız gerekiyor), CF-Initialize ve CF-Retrieve çifti ile oluşan checkout akışı, ve webhook IPN doğrulaması.
Bu rehberde incelediğimiz temel başlıklar:
iyzipayiçin TypeScriptdeclare moduletip tanımları- Checkout Form token oluşturma ve iframe render
- CF callback ve ödeme sonucu sorgulama
- Webhook imza doğrulama (HMAC-SHA256,
X-IYZ-SIGNATURE-V3) - Sub-merchant yapısıyla marketplace bölünmüş ödemeleri
- Sandbox test kartları ve sık karşılaşılan hatalar
E-ticaret projeniz için profesyonel ödeme entegrasyonu ve Next.js geliştirme konusunda destek almak isterseniz iletişime geçin ya da proje teklif formumuzu doldurun.


