Next.js ile Stripe Bağlantısı Nasıl Yapılır?
Next.js modern web uygulamaları geliştirmek için en popüler framework'lerden biri, Stripe ise en yaygın kullanılan ödeme platformlarından biri haline geldi. İkisi birlikte güçlü bir kombinasyon oluşturur: Next.js, güvenli sunucu tarafı kodu React bileşenlerinizin hemen yanında çalıştırmanıza izin verir ve bir ödeme entegrasyonunun ihtiyacı tam olarak budur. Gizli anahtarlar sunucuda kalır, ödeme oturumları route handler'larda oluşturulur ve webhook'lar sayfalarınızı sunan aynı uygulama tarafından karşılanır. Entegrasyonu doğru kurmak hızlı ve güvenli bir ödeme süreci ile her zaman doğru tamamlanan siparişler demektir; yanlış kurmak ise anahtarların açığa çıkmasına, manipüle edilmiş fiyatlara güvenilmesine veya ödemelerin tamamen kaçırılmasına yol açabilir. Bu rehberde Stripe'ı Next.js App Router ile adım adım bağlayacak, her aşama için kod örnekleri paylaşacağız.
Başlamadan Önce
Bu rehberi takip etmek için bir Stripe hesabına ve App Router kullanan bir Next.js projesine ihtiyacınız var. Stripe panelinde iki tür API anahtarı bulunur: tarayıcıda kullanılabilen yayınlanabilir anahtar (publishable key) ve asla sunucudan çıkmaması gereken gizli anahtar (secret key). Her hesabın ayrıca kendi anahtarlarına sahip bir test modu vardır; böylece entegrasyonun tamamı gerçek para hareketi olmadan geliştirilebilir. İşe resmi Stripe Node.js kütüphanesini ve sunucu kodunun yanlışlıkla istemci bileşenlerine aktarılmasını engelleyen server-only paketini kurarak başlayın.
npm install stripe server-onlyArdından anahtarlarınızı ortam değişkenlerine ekleyin. Gizli anahtar ve webhook anahtarı asla NEXT_PUBLIC_ önekini kullanmamalıdır, çünkü Next.js bu önekle başlayan her değişkeni tarayıcıya açar. Barındırılan Stripe Checkout ile yayınlanabilir anahtara ihtiyaç bile yoktur; bu anahtar yalnızca ileride Stripe Elements gömmek isterseniz gerekli hale gelir. Son olarak, yalnızca sunucuda kullanılabilen ortak bir Stripe istemcisi oluşturun.
# .env.local
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_SITE_URL=http://localhost:3000// lib/stripe.ts
import "server-only";
import Stripe from "stripe";
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
Checkout Session Oluşturmak
Stripe Checkout, ödeme almanın en hızlı ve en güvenli yoludur. Ödeme formunu kendiniz oluşturmak yerine sunucuda bir Checkout Session oluşturur ve müşteriyi kartları, dijital cüzdanları ve 3D Secure gibi ek doğrulamaları yöneten, Stripe tarafından barındırılan bir ödeme sayfasına yönlendirirsiniz. Next.js'te bu mantığın doğal yeri bir route handler'dır.
Burada her şeyden daha önemli bir kural var: tarayıcıdan gönderilen fiyata asla güvenmeyin. İstemci yalnızca müşterinin hangi ürünü veya planı istediğini söylemeli, gerçek fiyatı ise sunucu belirlemelidir. Aksi halde herkes isteği değiştirerek istediği tutarı ödeyebilir.
// app/api/checkout/route.ts
import { NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
// Prices are resolved on the server, never taken from the client
const PRICES: Record<string, string> = {
starter: "price_123",
pro: "price_456",
};
export async function POST(req: Request) {
const { plan } = await req.json();
const price = PRICES[plan];
if (!price) {
return NextResponse.json({ error: "Invalid plan" }, { status: 400 });
}
const session = await stripe.checkout.sessions.create({
mode: "payment",
line_items: [{ price, quantity: 1 }],
success_url: `${process.env.NEXT_PUBLIC_SITE_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
cancel_url: `${process.env.NEXT_PUBLIC_SITE_URL}/pricing`,
});
return NextResponse.json({ url: session.url });
}İstemci tarafında küçük bir bileşen bu route'u çağırır ve müşteriyi Stripe'ın döndürdüğü adrese yönlendirir. Böylece istemci tarafı hiçbir gizli mantık içermez.
// components/checkout-button.tsx
"use client";
export function CheckoutButton({ plan }: { plan: string }) {
async function handleClick() {
const res = await fetch("/api/checkout", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ plan }),
});
const { url } = await res.json();
window.location.href = url;
}
return <button onClick={handleClick}>Buy now</button>;
}
App Router'da Webhook'ları İşlemek
Başarı sayfasına yönlendirilmek, ödemenin yapıldığının kanıtı değildir. Müşteriler yönlendirilmeden önce sekmeyi kapatabilir ve bazı ödeme yöntemleri daha sonra onaylanır. Bu yüzden siparişler, tamamlanan bir ödeme, başarısız bir tahsilat veya bir iade gibi önemli bir olay gerçekleştiğinde Stripe'ın sunucunuza gönderdiği webhook'lara göre tamamlanmalıdır.
Her webhook'un gerçekten Stripe'tan geldiğinden emin olmak için imzası doğrulanmalıdır. İmza doğrulaması ham istek gövdesini gerektirir ve App Router bu noktada eski Pages Router'a göre işi kolaylaştırır: req.text() çağrısı, gövde ayrıştırmayı kapatmaya gerek kalmadan ham gövdeyi doğrudan döndürür. Stripe aynı olayı birden fazla kez iletebileceği için sipariş tamamlama mantığı da idempotent olmalıdır; yani bir olayı iki kez işlemek ikinci bir sipariş oluşturmamalıdır.
// app/api/webhooks/stripe/route.ts
import Stripe from "stripe";
import { stripe } from "@/lib/stripe";
export const runtime = "nodejs";
export async function POST(req: Request) {
const body = await req.text(); // raw body is required for signature checks
const signature = req.headers.get("stripe-signature");
let event: Stripe.Event;
try {
event = stripe.webhooks.constructEvent(
body,
signature!,
process.env.STRIPE_WEBHOOK_SECRET!
);
} catch {
return new Response("Invalid signature", { status: 400 });
}
if (event.type === "checkout.session.completed") {
const session = event.data.object as Stripe.Checkout.Session;
await fulfillOrder(session.id); // your own logic, must be idempotent
}
return new Response(null, { status: 200 });
}
Test Etmek ve Canlıya Geçmek
Stripe CLI, yerel ortamda webhook testini kolaylaştırır. Stripe'tan gelen olayları yerel geliştirme sunucunuza iletir ve ortam değişkenlerinizde kullanmanız için geçici bir webhook anahtarı üretir. Uygulamanızın nasıl tepki verdiğini görmek için belirli olayları istediğiniz an tetikleyebilirsiniz de.
stripe listen --forward-to localhost:3000/api/webhooks/stripe
stripe trigger checkout.session.completedBaşarılı ödemeleri, reddedilen kartları ve doğrulama gerektiren kartları simüle etmek için Stripe'ın test kart numaralarını kullanın. Tüm senaryolar doğru çalıştığında Stripe hesabınızı aktive edin, canlı webhook adresinizi panele kaydedin ve barındırma ortamınızda test anahtarlarını canlı anahtarlarla değiştirin. Test ve canlı anahtarları ayrı ortamlarda tutmak, geliştirme sırasında yanlışlıkla gerçek tahsilat yapılmasını önler.
Güvenlik ve En İyi Uygulamalar
En önemli güvenlik kuralı, gizli anahtarları kesinlikle sunucuda tutmaktır: ortam değişkenlerinde saklayın, asla NEXT_PUBLIC_ öneki eklemeyin ve hataları derleme aşamasında yakalamak için server-only kullanın. Yalnızca sınırlı erişime ihtiyaç duyan servisler için kısıtlı API anahtarları (restricted keys), sızan bir anahtarın verebileceği zararı azaltır. Kart bilgileri Stripe'ın barındırılan sayfasında girildiği için hassas ödeme verileri sunucularınıza hiç uğramaz; bu da PCI uyumluluk kapsamınızı önemli ölçüde daraltır.
Güvenilirlik için önemli nesneleri oluştururken idempotency anahtarları gönderin; böylece yeniden denenen istekler mükerrer tahsilat yaratmaz. Çift sipariş tamamlamayı önlemek için işlenen webhook olaylarının kimliklerini saklayın ve her ödeme olayını kayıt altına alın. Stripe kütüphanesi Node.js API'lerine dayandığı için webhook route handler'larını Node.js runtime'ında çalıştırın. Bu uygulamalar, çalışan bir ödeme sayfasını gerçek trafik altında ayakta kalan bir ödeme sistemine dönüştürür.
Sonuç
Stripe'ı Next.js ile bağlamak, tek bir kod tabanı içinde güvenli ve modern bir ödeme akışı sunar. Checkout Session'ları route handler'larda oluşturarak, fiyatları sunucuda belirleyerek, webhook'ları ham istek gövdesiyle doğrulayarak ve siparişleri idempotent biçimde tamamlayarak hem hızlı geliştirilen hem de üretimde güvenle çalışan bir entegrasyon kurarsınız. Bu temel; aboneliklere, müşteri portallarına ve gömülü ödeme formlarına doğal olarak genişletilebilir. İster basit bir ürün sayfası ister kapsamlı bir SaaS platformu yayına alıyor olun, ödeme entegrasyonunu doğru kurmak şarttır ve hem Stripe'ı hem de yazılım mimarisini anlayan deneyimli bir Next.js ekibiyle çalışmak, ödemelerinizin kalıcı bir temel üzerinde çalışmasını sağlar.