Yeni·E-Ticaret Pro Paketi Yayında — Entegre Ödeme, Stok ve Sipariş YönetimiBlog·2025'te Küçük İşletmeler İçin Web Tasarım TrendleriKampanya·Mayıs Ayına Özel %20 İndirim — Kartvizit & Başlangıç Paketleriİçgörü·Müşteri Projelerinde Dönüşüm Oranı Ortalama %40 Artış SağlandıBlog·SEO'ya Yeni Başlayanlar İçin Temel Rehber — Ücretsiz İndirHaber·Ankara'da Yeni Çözüm Ortaklıkları ile Hizmet Ağı GenişliyorGüncelleme·Tüm Projeler İçin Ücretsiz SSL, CDN ve Hız Optimizasyonu DahilYeni·Çözümler Sayfası Açıldı — Sektöre Özel Web ÇözümleriKampanya·Ücretsiz Web Sitesi Değerlendirmesi — Bugün Başvurİçgörü·Ortalama Proje Teslim Süresi: 14 Gün — GarantiliYeni·E-Ticaret Pro Paketi Yayında — Entegre Ödeme, Stok ve Sipariş YönetimiBlog·2025'te Küçük İşletmeler İçin Web Tasarım TrendleriKampanya·Mayıs Ayına Özel %20 İndirim — Kartvizit & Başlangıç Paketleriİçgörü·Müşteri Projelerinde Dönüşüm Oranı Ortalama %40 Artış SağlandıBlog·SEO'ya Yeni Başlayanlar İçin Temel Rehber — Ücretsiz İndirHaber·Ankara'da Yeni Çözüm Ortaklıkları ile Hizmet Ağı GenişliyorGüncelleme·Tüm Projeler İçin Ücretsiz SSL, CDN ve Hız Optimizasyonu DahilYeni·Çözümler Sayfası Açıldı — Sektöre Özel Web ÇözümleriKampanya·Ücretsiz Web Sitesi Değerlendirmesi — Bugün Başvurİçgörü·Ortalama Proje Teslim Süresi: 14 Gün — Garantili
ilkkod
Türkiye Ödeme Sistemleri

Next.js ile Craftgate Entegrasyonu: Tüm Bankalara Tek API ile Ödeme Alma

Craftgate'in resmi TypeScript SDK'sı ile Next.js 16 App Router'da ödeme entegrasyonu. Common Payment Page, taksit sorgulama ve akıllı yönlendirme adım adım.

İlker
24 Mart 2026
18 dk
Next.js ile Craftgate Entegrasyonu: Tüm Bankalara Tek API ile Ödeme Alma

Ö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. Craftgate fiyatlandırması için craftgate.io adresini ziyaret edin.

Türkiye'de bir e-ticaret sitesi veya SaaS ürünü geliştiriyorsanız, ödeme entegrasyonu kaçınılmaz bir adım. Tek bir bankaya bağlı sanal POS yerine birden fazla bankayı ve ödeme sağlayıcısını tek bir API üzerinden yönetmek istiyorsanız, ödeme orkestrasyonu tam size göre.

Craftgate, Ocak–Mayıs 2025 arasında 76.86 milyar TL işlem hacmi işlemiş ve Türkiye'nin en hızlı büyüyen ödeme platformlarından biri haline gelmiştir. En önemli avantajlarından biri: resmi @craftgate/craftgate npm paketi tam TypeScript desteği sunar — iyzipay veya resmi PayTR paketi bulunmaması ile kıyaslandığında bu büyük bir geliştirici deneyimi farkı yaratır.

Bu rehberde Next.js 16 App Router kullanarak Craftgate entegrasyonunu adım adım ele alacağız.

Craftgate Nedir? Ödeme Orkestrasyonu Konsepti

Geleneksel sanal POS entegrasyonunda tek bir bankaya bağlanırsınız. Başarısız ödemede yapabileceğiniz fazla bir şey yoktur. Ödeme orkestrasyonu ise birden fazla banka ve ödeme sağlayıcısını tek bir API arkasına toplar:

  • Akıllı yönlendirme (Smart Routing): Ödeme girişimi başarısız olursa, sistem otomatik olarak alternatif bir bankaya yönlendirir (failover routing)
  • Komisyon optimizasyonu: Yapılandırılmış banka POS'ları arasından en avantajlı olanı seçer
  • Tek entegrasyon: İş mantığınızı değiştirmeden arka planda banka değiştirebilirsiniz
  • Sub-merchant desteği: Marketplace kuruyorsanız satıcı bazlı ödeme dağıtımı yapabilirsiniz

Craftgate'i özellikle ajanslar ve freelancer'lar için ideal yapan şey: tek bir entegrasyonla birden fazla müşterinizin e-ticaret sitesini yönetebilirsiniz.

Craftgate vs Diğer Sağlayıcılar — Ne Zaman Craftgate?

KriterCraftgateiyzicoPayTR
Resmi npm paketi@craftgate/craftgateiyzipay❌ Resmi yok
TypeScript desteği✅ Tam (%65.4 TS)❌ YokTopluluk (%99 TS)
Sandboxsandbox-api.craftgate.io✅ Vartest_mode=1
Ödeme orkestrasyonu✅ Temel özellik
Sub-merchant✅ Tam destek✅ VarSınırlı

Senaryo bazlı değerlendirme için Sanal POS Maliyetleri 2025 yazımıza da bakabilirsiniz.

Ön Koşullar

  • Node.js 18+ veya Bun 1.x
  • Next.js 16 App Router projesi
  • Craftgate sandbox hesabı

Craftgate hesabı için vergi numarası olan bir şahıs şirketi veya limited şirket gerektirir. Bireysel başvuru yapılamaz. Başvuru süreci ve gerekli belgeler için güncel bilgiyi craftgate.io adresinden edinin.

SDK Kurulumu

bun add @craftgate/craftgate

@craftgate/craftgate sürüm 1.0.65 itibarıyla ayrı bir @types paketi kurmanıza gerek yoktur — tip tanımları pakete dahildir.

Ortam Değişkenleri

.env.local dosyasına ekleyin:

CRAFTGATE_API_KEY=your-api-key-here CRAFTGATE_SECRET_KEY=your-secret-key-here CRAFTGATE_BASE_URL=https://sandbox-api.craftgate.io NEXT_PUBLIC_SITE_URL=http://localhost:3000

Production'a geçerken CRAFTGATE_BASE_URL'i https://api.craftgate.io olarak güncelleyin.

lib/env.ts ile Zod doğrulaması:

import { z } from "zod" const envSchema = z.object({ CRAFTGATE_API_KEY: z.string().min(1), CRAFTGATE_SECRET_KEY: z.string().min(1), CRAFTGATE_BASE_URL: z.string().url().default("https://sandbox-api.craftgate.io"), NEXT_PUBLIC_SITE_URL: z.string().url(), }) export const env = envSchema.parse(process.env)

Craftgate Client

Tek bir yerde başlatıp export edin; her Route Handler'da tekrar başlatmayın:

// lib/craftgate.ts import Craftgate from "@craftgate/craftgate" import { env } from "@/lib/env" export const craftgate = new Craftgate.Client({ apiKey: env.CRAFTGATE_API_KEY, secretKey: env.CRAFTGATE_SECRET_KEY, baseUrl: env.CRAFTGATE_BASE_URL, })

Common Payment Page ile Ödeme Akışı

Craftgate'in önerilen entegrasyon yöntemi Common Payment Page (CPP)'dir. Ödeme sayfasını sıfırdan oluşturmanıza gerek yoktur; Craftgate'in hosted sayfasına kullanıcıyı yönlendirirsiniz.

Akış 3 adımdan oluşur:

  1. Server-side: Ödeme token'ı oluştur → pageUrl ve token al
  2. Client-side: Kullanıcıyı pageUrl'e yönlendir
  3. Callback: Craftgate callbackUrl'e sonucu POST atar

Step 1 — Ödeme Başlatma Route Handler

// app/api/craftgate/init/route.ts import { craftgate } from "@/lib/craftgate" import { env } from "@/lib/env" import { NextResponse } from "next/server" interface CartItem { id: string name: string price: number quantity: number } export async function POST(request: Request) { const body = await request.json() const { orderId, items }: { orderId: string; items: CartItem[] } = body // Kritik: items toplamı === price olmalı const totalPrice = items.reduce((sum, item) => sum + item.price * item.quantity, 0) const response = await craftgate.payment().initCheckoutPayment({ price: totalPrice, paidPrice: totalPrice, // Taksitsiz ödemede eşit; faiz eklenirse paidPrice > price olabilir currency: "TRY", callbackUrl: `${env.NEXT_PUBLIC_SITE_URL}/api/craftgate/callback`, conversationId: orderId, items: items.map((item) => ({ externalId: item.id, name: item.name, price: item.price * item.quantity, itemType: "PHYSICAL_GOODS" as const, })), }) if (response.errors) { return NextResponse.json( { error: response.errors.errorDescription }, { status: 400 }, ) } return NextResponse.json({ token: response.token, pageUrl: response.pageUrl, }) }

Önemli detay: items[] dizisindeki fiyatların toplamı price değerine tam olarak eşit olmalıdır. Aksi hâlde API hata döner.

Step 2 — Client-Side Yönlendirme

// components/checkout-button.tsx "use client" import { useState } from "react" interface CartItem { id: string name: string price: number quantity: number } interface CheckoutButtonProps { orderId: string items: CartItem[] } export function CheckoutButton({ orderId, items }: CheckoutButtonProps) { const [loading, setLoading] = useState(false) const handleCheckout = async () => { setLoading(true) try { const res = await fetch("/api/craftgate/init", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ orderId, items }), }) const data = await res.json() if (data.pageUrl) { window.location.href = data.pageUrl } } finally { setLoading(false) } } return ( <button type="button" onClick={handleCheckout} disabled={loading} className="h-12 px-6 bg-blue-600 text-white rounded-lg disabled:opacity-50" > {loading ? "Yönlendiriliyor..." : "Ödemeye Geç"} </button> ) }

Step 3 — Callback Handler

Ödeme tamamlandığında (başarılı veya başarısız) Craftgate callbackUrl'e token içeren bir form POST atar. Bu token ile ödeme sonucunu sunucu tarafında sorgularsınız:

// app/api/craftgate/callback/route.ts import { craftgate } from "@/lib/craftgate" import { db } from "@/lib/db" import { orders } from "@/lib/db/schema" import { eq } from "drizzle-orm" import { NextResponse } from "next/server" export async function POST(request: Request) { const formData = await request.formData() const token = formData.get("token") as string | null if (!token) { return new Response("token missing", { status: 400 }) } // Token ile ödeme sonucunu sorgula const paymentResult = await craftgate.payment().retrieveCheckoutPayment(token) if (paymentResult.paymentStatus === "SUCCESS") { // Drizzle ORM ile sipariş güncelle await db .update(orders) .set({ status: "paid", paymentId: String(paymentResult.id ?? ""), paidAt: new Date(), }) .where(eq(orders.externalId, paymentResult.conversationId ?? "")) return NextResponse.redirect( `${process.env.NEXT_PUBLIC_SITE_URL}/siparis/tamamlandi`, ) } const errorReason = paymentResult.paymentError?.errorDescription ?? "unknown" return NextResponse.redirect( `${process.env.NEXT_PUBLIC_SITE_URL}/siparis/hata?reason=${encodeURIComponent(errorReason)}`, ) }

Not: Callback URL'inizin HTTPS olması ve harici erişime açık olması gerekir. Local geliştirmede ngrok kullanın.

Taksit Sorgulama API

Craftgate'in taksit API'si, kullanıcının kartının ilk 8 hanesi (binNumber) ile dinamik taksit seçenekleri döndürür. Kullanıcı kart numarasını girerken bu API'yi çağırarak gerçek zamanlı taksit tablosu gösterebilirsiniz.

binNumber opsiyoneldir. Gönderilmezse genel taksit seçenekleri; gönderilirse kullanıcının kartına özgü seçenekler döner.

Taksit Sorgulama Route Handler

// app/api/craftgate/installments/route.ts import { craftgate } from "@/lib/craftgate" import { NextResponse } from "next/server" export async function GET(request: Request) { const { searchParams } = new URL(request.url) const price = searchParams.get("price") const binNumber = searchParams.get("binNumber") if (!price) { return NextResponse.json({ error: "price parametresi gerekli" }, { status: 400 }) } const result = await craftgate.installment().retrieveInstallments({ price: Number.parseFloat(price), currency: "TRY", ...(binNumber ? { binNumber } : {}), }) return NextResponse.json({ installmentPrices: result.installmentPrices ?? [], cardBrand: result.cardBrand, bankName: result.bankName, cardType: result.cardType, }) }

Client-Side Taksit Bileşeni

// components/installment-options.tsx "use client" import { useEffect, useState } from "react" interface InstallmentPrice { installmentNumber: number totalPrice: number installmentPrice: number } interface InstallmentOptionsProps { binNumber: string price: number } export function InstallmentOptions({ binNumber, price }: InstallmentOptionsProps) { const [installments, setInstallments] = useState<InstallmentPrice[]>([]) const [bankName, setBankName] = useState<string>("") useEffect(() => { if (binNumber.length < 8) { setInstallments([]) return } fetch(`/api/craftgate/installments?price=${price}&binNumber=${binNumber}`) .then((r) => r.json()) .then((data) => { setInstallments(data.installmentPrices ?? []) setBankName(data.bankName ?? "") }) }, [binNumber, price]) if (installments.length === 0) return null return ( <div className="mt-4 rounded-lg border border-gray-200 p-4"> {bankName && ( <p className="mb-2 text-xs text-gray-500">{bankName} taksit seçenekleri</p> )} <div className="space-y-2"> {installments.map((inst) => ( <div key={inst.installmentNumber} className="flex justify-between text-sm"> <span> {inst.installmentNumber === 1 ? "Tek çekim" : `${inst.installmentNumber} taksit`} </span> <span className="font-medium"> {inst.installmentNumber === 1 ? `${inst.totalPrice.toFixed(2)} TL` : `${inst.installmentPrice.toFixed(2)} TL × ${inst.installmentNumber}`} </span> </div> ))} </div> </div> ) }

Akıllı Yönlendirme (Smart Routing)

Craftgate'in en güçlü özelliklerinden biri Smart Routing — failover ile otomatik banka değişimi.

Nasıl çalışır:

  1. Craftgate ödeme girişimini alır
  2. Merchant panelinde yapılandırılmış banka POS'ları arasından en uygun olanı seçer
  3. Ödeme başarısız olursa alternatif bankaya otomatik olarak failover yapar
  4. Tüm bu akış size şeffaf — kodunuzda herhangi bir değişiklik gerektirmez

Bu özelliği aktif hale getirmek için Craftgate merchant panelinde birden fazla banka POS tanımlamanız gerekir. Kod tarafında tek bir entegrasyon yeterlidir.

Sub-Merchant (Marketplace) Entegrasyonu

Bir marketplace veya platform kuruyorsanız, Craftgate'in sub-merchant desteği satıcı başına ödeme dağıtımını yönetmenizi sağlar.

Sub-Merchant Oluşturma (Tek Seferlik)

// app/api/craftgate/sub-merchant/route.ts import { craftgate } from "@/lib/craftgate" import { NextResponse } from "next/server" export async function POST(request: Request) { const body = await request.json() const member = await craftgate.member().createMember({ memberType: "PERSONAL", name: body.name, email: body.email, iban: body.iban, contactName: body.contactName, contactSurname: body.contactSurname, address: body.address, taxOffice: body.taxOffice, identityNumber: body.identityNumber, }) return NextResponse.json({ memberId: member.id }) }

Ödeme Sırasında Sub-Merchant Kalemi

const response = await craftgate.payment().initCheckoutPayment({ price: totalPrice, paidPrice: totalPrice, currency: "TRY", callbackUrl: callbackUrl, items: [ { externalId: "urun-1", name: "Ürün Adı", price: 100, itemType: "PHYSICAL_GOODS" as const, subMerchantMemberId: saticiMemberId, subMerchantMemberPrice: 85, // Satıcıya gidecek tutar (15 TL platform kesintisi) }, ], })

Escrow tamamlandığında yerleşim için createInstantWalletSettlement() metodunu kullanabilirsiniz.

İade İşlemleri

// app/api/craftgate/refund/route.ts import { craftgate } from "@/lib/craftgate" import { NextResponse } from "next/server" export async function POST(request: Request) { const { paymentTransactionId, refundAmount } = await request.json() const refund = await craftgate.payment().refundPaymentTransaction({ paymentTransactionId: paymentTransactionId, refundPrice: refundAmount, currency: "TRY", conversationId: `refund-${Date.now()}`, }) if (refund.status === "SUCCESS") { return NextResponse.json({ success: true, refundId: refund.id }) } return NextResponse.json( { error: refund.errors?.errorDescription ?? "İade başarısız" }, { status: 400 }, ) }

Test Ortamı

Craftgate sandbox ortamı https://sandbox-api.craftgate.io adresinde çalışır. Sandbox API anahtarlarını Craftgate merchant panel üzerinden alabilirsiniz.

Sandbox'ta gerçek Craftgate ödeme sayfasına yönlendirilirsiniz. Test kart numaraları sandbox panelinde mevcuttur.

Local geliştirme için callback URL:

Callback URL'inizin dışarıdan erişilebilir olması gerekir. ngrok ile çözün:

bunx ngrok http 3000 # Çıktıdaki URL'i .env.local'a ekleyin: # NEXT_PUBLIC_SITE_URL=https://xxxx.ngrok-free.app

Test sırasında dikkat edilecekler:

  • CRAFTGATE_BASE_URL'in sandbox URL (https://sandbox-api.craftgate.io) olduğunu doğrulayın
  • Her test isteğinde conversationId değerini benzersiz tutun
  • items[] toplamının price'a eşit olduğunu kontrol edin

Production Geçiş Kontrol Listesi

  • CRAFTGATE_BASE_URL'i https://api.craftgate.io olarak güncelleyin
  • Production API anahtarlarını hosting ortam değişkenlerine ekleyin
  • Callback URL'inizin HTTPS olduğunu doğrulayın
  • Callback endpoint'ini beklenmedik POST isteklerine karşı test edin
  • Callback alınamayan siparişler için fallback sorgulama mekanizması ekleyin (örn. Vercel Cron ile pending siparişleri kontrol edin)
  • Hata izleme (Sentry vb.) kurun — ödeme hatalarını takip edin
  • Ödeme başarısız senaryosunda kullanıcıya anlamlı Türkçe hata mesajı gösterin
  • Merchant panelinde Smart Routing için birden fazla banka POS yapılandırın

Sıkça Sorulan Sorular

Craftgate hesabı açmak için ne gerekiyor?

Vergi numarası olan şahıs şirketi veya limited şirket gerektirir. Bireysel başvuru yapılamaz. Güncel başvuru koşulları için craftgate.io/tr adresini ziyaret edin.

Common Payment Page ile kendi ödeme formumu tasarlayabilir miyim?

CPP, Craftgate'in hosted sayfasıdır. Kısmi özelleştirme (logo, renk) mümkündür ancak kapsamı sınırlıdır. Tam özel ödeme formu isterseniz Craftgate'in Direct API'sini kullanabilirsiniz; bu yaklaşım PCI uyumluluğu açısından daha fazla sorumluluk gerektirir.

price ile paidPrice farkı nedir?

price sepet toplamıdır (taksitsiz, faizsiz fiyat). paidPrice ise tahsil edilecek gerçek tutardır. Taksitli ödemelerde banka faizi eklenerek paidPrice > price olabilir. Tek çekim ödemelerde ikisi eşit olmalıdır.

binNumber olmadan taksit sorgulama yapılabilir mi?

Evet, binNumber opsiyoneldir. Gönderilmezse genel taksit seçenekleri döner. Kullanıcı kart numarasının ilk 8 hanesini girdikten sonra binNumber ile sorgu yapmak, kullanıcının kartına özgü gerçek taksit seçeneklerini göstereceğinden daha iyi bir deneyim sunar.

Webhook / IPN desteği var mı?

Evet. Craftgate, callbackUrl mekanizması dışında ayrıca webhook desteği sunar. Merchant panelinden webhook endpoint'i yapılandırabilirsiniz. Ödeme durumu değişimlerinde (başarı, başarısız, iade) anlık bildirim alabilirsiniz.

Craftgate yabancı para birimi destekliyor mu?

Evet. Desteklenen para birimleri: TRY, USD, EUR, GBP, CNY, ARS, BRL, AED, IQD, AZN, KZT, KWD, SAR, BHD, RUB, JPY, EGP. Yurt dışı kartlardan yabancı para birimi üzerinden ödeme alabilirsiniz.

Smart Routing için ek kod yazmak gerekiyor mu?

Hayır. Smart Routing tamamen Craftgate altyapısında çalışır. Merchant panelinde birden fazla banka POS yapılandırdığınızda otomatik devreye girer. Kodunuzda herhangi bir değişiklik gerekmez.

Sonuç

Craftgate, ödeme orkestrasyonu konseptiyle çoklu banka yönetimini tek API'ye indirgeyerek Türkiye'de benzersiz bir çözüm sunuyor. Resmi TypeScript SDK'sı, CPP ile hızlı entegrasyon, smart routing ve sub-merchant desteği — özellikle birden fazla müşteri için e-ticaret geliştiren ajanslar ve ödeme başarı oranını artırmak isteyen projeler için güçlü bir seçenek.

Bu projede de PayTR ile birlikte ödeme entegrasyonu deneyimi mevcut. Projenize ödeme entegrasyonu eklemeye ihtiyaç duyuyorsanız iletişime geçin.

İlgili yazılar:

Paylaş: