Next.js App Router ile PayTR Ödeme Entegrasyonu

Konu ile ilgili dökümantasyona ve örnek kullanıma erişebilmeniz için paytr-next repoma gidebilirsiniz.

E-ticaret dünyasında güvenli ve hızlı ödeme işlemleri, kullanıcı deneyimi ve işletme başarısı için kritik öneme sahiptir. PayTR, Türkiye'nin önde gelen ödeme sistemlerinden biri olarak, sanal POS ve ödeme çözümleri sunar.

Next.js'in App Router özelliği ile modern, sunucu merkezli web uygulamaları geliştirirken, PayTR entegrasyonu yapmak hem güçlü hem de esnek bir çözüm sunar. Bu makalemde, Next.js App Router kullanarak PayTR ödeme entegrasyonunun nasıl gerçekleştirileceğini adım adım açıklayacağım.

1. PayTR Nedir ve Neden Kullanılır?

PayTR, 6493 sayılı yasa kapsamında TCMB tarafından denetlenen, PCI-DSS uyumlu bir ödeme ve elektronik para kuruluşudur. Kredi kartı, banka kartı ve banka transferi gibi çeşitli ödeme yöntemlerini destekler. Avantajları arasında hızlı entegrasyon, düşük komisyon oranları, 3D Secure desteği ve kart saklama gibi özellikler bulunur.

Next.js App Router, sunucu bileşenleri ve istemci bileşenlerini bir arada kullanarak performansı optimize eden modern bir yapı sunar. Bu rehberde, PayTR'ın Direkt API'sini Next.js App Router ile entegre ederek ödeme işlemlerini nasıl gerçekleştireceğinizi anlatacağım.

2. Ön Gereksinimler

Başlamadan önce aşağıdaki gereksinimlere sahip olduğunuzdan emin olun:

  • PayTR Mağaza Hesabı: PayTR'dan bir üye işyeri hesabı oluşturun. Başvuru için PayTR web sitesini ziyaret edebilirsiniz.
  • Next.js Projesi: Next.js 13 veya üstü bir sürümde App Router kullanılan bir proje.
  • Gerekli API Bilgileri: PayTR Mağaza Paneli'nden temin edeceğiniz Merchant ID, Merchant Key ve Merchant Salt.
  • Node.js ve npm/yarn: Geliştirme ortamınızda yüklü olmalı.
  • Temel React ve JavaScript Bilgisi: Next.js ve React bileşenleri hakkında bilgi sahibi olmanız faydalı olacaktır.

3. Proje Kurulumu

Next.js projenizi oluşturmadıysanız, aşağıdaki komutla yeni bir proje başlatabilirsiniz:


    npx create-next-app@latest my-paytr-app
    cd my-paytr-app

Projenizde App Router'ı kullanmak için app dizini altında yapılandırmalar yapacağız. Ayrıca, PayTR ile iletişim kurmak için HTTP istekleri gönderebilmek adına axios veya fetch kullanacağız. axios kullanmak isterseniz, aşağıdaki komutla kurabilirsiniz:


    npm install axios

4. PayTR Entegrasyon Süreci

PayTR Direkt API entegrasyonu, birkaç temel adımdan oluşur:

  1. Ödeme Formu Oluşturma: Kullanıcıdan kart bilgilerini almak için istemci tarafında bir form oluşturacağız.
  2. Token Üretimi: PayTR API'sine gönderilecek güvenli bir token oluşturacağız.
  3. Ödeme İsteği Gönderme: Kullanıcı bilgilerini ve token'ı PayTR'a POST metoduyla göndereceğiz.
  4. Ödeme Sonucunu İşleme: Başarılı veya başarısız ödeme sonucunu işlemek için bir bildirim URL'si tanımlayacağız.
4.1. Ödeme Formu Oluşturma

İstemci tarafında, kullanıcıdan kart bilgilerini alacak bir React bileşeni oluşturuyoruz. Next.js App Router'da istemci bileşenleri için "use client" direktifini kullanacağız.

Dosya: app/payment/page.tsx


    "use client";

    import { useState } from "react";
    import axios from "axios";

    export default function PaymentPage() {
      const [formData, setFormData] = useState({
        cardNumber: "",
        expiryDate: "",
        cvv: "",
        cardHolder: "",
      });

      const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
        setFormData({ ...formData, [e.target.name]: e.target.value });
      };

      const handleSubmit = async (e: React.FormEvent) => {
        e.preventDefault();
        try {
          const response = await axios.post("/api/paytr", {
            ...formData,
            amount: 1000, // Örnek: 10.00 TL (PayTR'da tutar kuruş cinsinden gönderilir)
          });
          if (response.data.token) {
            // PayTR ödeme sayfasına yönlendirme
            const form = document.createElement("form");
            form.method = "POST";
            form.action = "https://www.paytr.com/odeme";
            const input = document.createElement("input");
            input.type = "hidden";
            input.name = "paytr_token";
            input.value = response.data.token;
            form.appendChild(input);
            document.body.appendChild(form);
            form.submit();
          }
        } catch (error) {
          console.error("Ödeme hatası:", error);
        }
      };

      return (
        <div className="max-w-md mx-auto mt-10">
          <h1 className="text-2xl font-bold mb-4">Ödeme Sayfası</h1>
          <form onSubmit={handleSubmit} className="space-y-4">
            <div>
              <label>Kart Numarası</label>
              <input
                type="text"
                name="cardNumber"
                value={formData.cardNumber}
                onChange={handleChange}
                className="w-full p-2 border rounded"
                placeholder="1234 5678 9012 3456"
              />
            </div>
            <div>
              <label>Son Kullanım Tarihi</label>
              <input
                type="text"
                name="expiryDate"
                value={formData.expiryDate}
                onChange={handleChange}
                className="w-full p-2 border rounded"
                placeholder="MM/YY"
              />
            </div>
            <div>
              <label>CVV</label>
              <input
                type="text"
                name="cvv"
                value={formData.cvv}
                onChange={handleChange}
                className="w-full p-2 border rounded"
                placeholder="123"
              />
            </div>
            <div>
              <label>Kart Sahibi</label>
              <input
                type="text"
                name="cardHolder"
                value={formData.cardHolder}
                onChange={handleChange}
                className="w-full p-2 border rounded"
                placeholder="Ad Soyad"
              />
            </div>
            <button
              type="submit"
              className="w-full bg-blue-500 text-white p-2 rounded"
            >
              Ödeme Yap
            </button>
          </form>
        </div>
      );
    }

Bu kod, kullanıcıdan kart bilgilerini alan bir form oluşturur ve bu bilgileri bir API rotasına gönderir. PayTR, kart bilgilerinin doğrudan kendi sunucusuna POST edilmesini istediğinden, kullanıcıyı PayTR ödeme sayfasına yönlendireceğiz.

4.2. Token Üretimi (Sunucu Tarafı)

PayTR, ödeme işlemini başlatmak için bir token gerektirir. Bu token, sunucu tarafında oluşturulur ve PayTR'ın HMAC-SHA256 algoritması kullanılarak şifrelenir.

Dosya: app/api/paytr/route.ts


    import { NextRequest, NextResponse } from "next/server";
    import crypto from "crypto";

    export async function POST(request: NextRequest) {
      const body = await request.json();
      const {
        cardNumber,
        expiryDate,
        cvv,
        cardHolder,
        amount,
      } = body;

      // PayTR API bilgileri (Mağaza Paneli'nden alın)
      const merchant_id = "YOUR_MERCHANT_ID";
      const merchant_key = "YOUR_MERCHANT_KEY";
      const merchant_salt = "YOUR_MERCHANT_SALT";

      // Örnek sipariş ve kullanıcı bilgileri
      const merchant_oid = `ORD${Date.now()}`; // Benzersiz sipariş numarası
      const email = "user@example.com";
      const payment_amount = amount; // Kuruş cinsinden
      const user_ip = request.headers.get("x-forwarded-for") || "127.0.0.1";
      const user_name = cardHolder;
      const user_address = "Örnek Adres";
      const user_phone = "5305555555";
      const merchant_ok_url = "http://your-site.com/success";
      const merchant_fail_url = "http://your-site.com/fail";
      const user_basket = JSON.stringify([
        ["Örnek Ürün", (amount / 100).toFixed(2), 1],
      ]);
      const test_mode = "1"; // Test modu: 1, Canlı mod: 0
      const debug_on = "1";
      const timeout_limit = "15";
      const currency = "TL";
      const card_type = "bonus"; // Örnek kart tipi
      const installment_count = "0"; // Tek çekim

      // Hash oluşturma
      const hash_str = `${merchant_id}${user_ip}${merchant_oid}${email}${payment_amount}${user_basket}${test_mode}${installment_count}${currency}`;
      const paytr_token = crypto
        .createHmac("sha256", merchant_key)
        .update(hash_str)
        .digest();
      const token = Buffer.concat([paytr_token, Buffer.from(merchant_salt)])
        .toString("base64");

      // PayTR'a gönderilecek veri
      const paytrData = {
        merchant_id,
        merchant_oid,
        email,
        payment_amount,
        user_ip,
        user_name,
        user_address,
        user_phone,
        merchant_ok_url,
        merchant_fail_url,
        user_basket,
        test_mode,
        debug_on,
        timeout_limit,
        currency,
        card_type,
        installment_count,
        paytr_token: token,
      };

      return NextResponse.json({ token });
    }

Bu kod, PayTR token'ını oluşturur ve istemciye döndürür. Token, PayTR ödeme sayfasına yönlendirme için kullanılır. Önemli not: Kart bilgileri asla kendi sunucunuza POST edilmemeli, doğrudan PayTR'a gönderilmelidir.

4.3. Ödeme Sonucunu İşleme

PayTR, ödeme sonucunu bildirmek için bir Bildirim URL'si kullanır. Bu URL, ödeme işleminin başarılı veya başarısız olduğunu bildirir.

Dosya: app/api/paytr-callback/route.ts


    import { NextRequest, NextResponse } from "next/server";
    import crypto from "crypto";

    export async function POST(request: NextRequest) {
      const body = await request.formData();
      const merchant_oid = body.get("merchant_oid") as string;
      const status = body.get("status") as string;
      const total_amount = body.get("total_amount") as string;
      const hash = body.get("hash") as string;

      const merchant_key = "YOUR_MERCHANT_KEY";
      const merchant_salt = "YOUR_MERCHANT_SALT";

      // Hash doğrulama
      const expected_hash = crypto
        .createHmac("sha256", merchant_key)
        .update(`${merchant_oid}${merchant_salt}${status}${total_amount}`)
        .digest("base64");

      if (hash === expected_hash) {
        if (status === "success") {
          // Ödeme başarılı, siparişi onaylayın
          console.log(`Sipariş ${merchant_oid} başarılı! Tutar: ${total_amount}`);
          return NextResponse.json({ status: "success" });
        } else {
          // Ödeme başarısız
          console.log(`Sipariş ${merchant_oid} başarısız!`);
          return NextResponse.json({ status: "failed" });
        }
      } else {
        // Hash doğrulanamadı
        return NextResponse.json({ error: "Geçersiz hash" }, { status: 400 });
      }
    }

Bu rota, PayTR'dan gelen bildirimi doğrular ve sipariş durumunu günceller. Başarılı ödemelerde siparişi onaylayabilir, başarısız ödemelerde kullanıcıyı bilgilendirebilirsiniz.

4.4. Başarılı ve Başarısız Yönlendirme Sayfaları

Ödeme işlemi tamamlandığında, kullanıcı merchant_ok_url veya merchant_fail_url adreslerine yönlendirilir.

Dosya: app/success/page.tsx


    export default function SuccessPage() {
      return (
        <div className="max-w-md mx-auto mt-10">
          <h1 className="text-2xl font-bold text-green-600">Ödeme Başarılı!</h1>
          <p>Siparişiniz başarıyla tamamlandı. Teşekkür ederiz!</p>
        </div>
      );
    }

Dosya: app/fail/page.tsx


    export default function FailPage() {
      return (
        <div className="max-w-md mx-auto mt-10">
          <h1 className="text-2xl font-bold text-red-600">Ödeme Başarısız</h1>
          <p>Ödeme işlemi sırasında bir hata oluştu. Lütfen tekrar deneyin.</p>
        </div>
      );
    }

5. Test ve Canlı Moda Geçiş

PayTR, test işlemleri için test kart bilgileri sağlar. Bu bilgileri PayTR Mağaza Paneli > Destek & Kurulum > Developer Portal > Direkt API Test Kart Bilgileri sayfasından alabilirsiniz. Test işlemi başarılı olduğunda, PayTR Mağaza Paneli'nden canlı moda geçiş talebi gönderebilirsiniz.

Test Kart Örneği:

  • Kart Numarası: 4111111111111111
  • Son Kullanım: 12/26
  • CVV: 123

6. En İyi Uygulamalar ve İpuçları

  • Güvenlik: Kart bilgilerinin asla kendi sunucunuza kaydedilmediğinden emin olun. PayTR, PCI-DSS uyumludur ve kart bilgilerini güvenli bir şekilde işler.
  • Suspense Kullanımı: Next.js App Router'da, ödeme formunun yüklenmesi sırasında bir yükleme durumu göstermek için <Suspense> bileşenini kullanabilirsiniz.
  • Hata Yönetimi: Ödeme hatalarını kullanıcıya anlaşılır bir şekilde bildirin. Örneğin, merchant_fail_url sayfasında hata mesajını gösterebilirsiniz.
  • Ortam Değişkenleri: Merchant ID, Merchant Key ve Merchant Salt gibi hassas bilgileri .env dosyasında saklayın.

Dosya: .env.local


    PAYTR_MERCHANT_ID=YOUR_MERCHANT_ID
    PAYTR_MERCHANT_KEY=YOUR_MERCHANT_KEY
    PAYTR_MERCHANT_SALT=YOUR_MERCHANT_SALT

  • Asenkron İşlemler: PayTR, asenkron bir ödeme sistemi kullanır. Ödeme sonucu merchant_ok_url'e veri göndermez; bu nedenle sipariş onayı için mutlaka Bildirim URL'sini kullanın.

Sonuç

Next.js App Router ile PayTR entegrasyonu, modern web uygulamaları için güçlü ve güvenli bir ödeme çözümü sunar. Bu rehberde, ödeme formu oluşturma, token üretimi, ödeme sonucunu işleme ve canlı moda geçiş gibi temel adımları detaylı bir şekilde ele aldım. PayTR'ın sunduğu esnek API ve Next.js'in sunucu bileşenleri sayesinde, performans odaklı ve kullanıcı dostu bir ödeme deneyimi oluşturabilirsiniz.

Sorularınız varsa veya daha fazla özelleştirme gerekiyorsa, PayTR Mağaza Paneli'ni veya PayTR Developer Portal'ı ziyaret edebilirsiniz. Ayrıca, Next.js topluluğundan destek almak için Next.js GitHub sayfasını inceleyebilirsiniz.


06 Ara 2025
Yorum