WooCommerce Ödeme Sayfasına Özel Alan Ekleme

E-ticaret sitelerinde müşteri deneyimini iyileştirmenin en etkili yollarından biri, ödeme sayfasını özelleştirmektir. WooCommerce’in sunduğu standart alanlar çoğu zaman yeterli olsa da, gerçek dünya projelerinde “müşteri TC kimlik numarası alalım”, “fatura bilgilerini özelleştirelim” veya “teslimat için özel not alanı ekleyelim” gibi ihtiyaçlar kaçınılmaz olarak ortaya çıkar. İşte bu noktada functions.php dosyası imdadımıza yetişiyor.

Bu yazıda WooCommerce ödeme sayfasına özel alan eklemenin tüm adımlarını, farklı senaryoları ve production ortamında dikkat edilmesi gereken noktaları ele alacağız.

Temel Yapıyı Anlamak

WooCommerce checkout sayfası birkaç temel hook ile yönetilir. Bu hook’ları doğru anlamadan yazdığınız kod ya çalışmaz ya da verileri kaydetmez.

En çok kullanacağımız hook’lar şunlar:

  • woocommerce_checkout_fields: Mevcut alanları filtrelemek veya düzenlemek için
  • woocommerce_checkout_after_customer_details: Müşteri detaylarının altına alan eklemek için
  • woocommerce_checkout_before_order_review: Sipariş özetinden önce alan eklemek için
  • woocommerce_checkout_order_processed: Sipariş işlendiğinde veriyi kaydetmek için
  • woocommerce_admin_order_data_after_billing_address: Admin panelinde siparişi görüntülemek için

Alanlar genellikle üç bölgeye eklenir: billing (fatura), shipping (teslimat) ve order (sipariş). Her bölgenin kendi field dizisi var ve bunları birbirinden bağımsız yönetebilirsiniz.

Basit Bir Metin Alanı Eklemek

En temel senaryodan başlayalım. Diyelim ki müşteriden TC kimlik numarası almak istiyorsunuz. E-fatura düzenleyen firmalar için bu oldukça yaygın bir gereksinim.

// functions.php dosyasına ekleyin
add_filter( 'woocommerce_checkout_fields', 'ozel_tc_kimlik_alani_ekle' );

function ozel_tc_kimlik_alani_ekle( $fields ) {
    $fields['billing']['billing_tc_kimlik'] = array(
        'type'        => 'text',
        'label'       => 'TC Kimlik Numarası',
        'placeholder' => 'XXXXXXXXXXX',
        'required'    => false,
        'class'       => array( 'form-row-wide' ),
        'clear'       => true,
        'priority'    => 25,
    );
    return $fields;
}

Bu kod alanı sayfada gösterir ama veriyi henüz kaydetmez. Kaydetmek için ayrı bir hook gerekiyor:

// Sipariş meta verisine kaydet
add_action( 'woocommerce_checkout_order_processed', 'tc_kimlik_kaydet', 10, 1 );

function tc_kimlik_kaydet( $order_id ) {
    if ( ! empty( $_POST['billing_tc_kimlik'] ) ) {
        $tc_kimlik = sanitize_text_field( $_POST['billing_tc_kimlik'] );
        update_post_meta( $order_id, '_billing_tc_kimlik', $tc_kimlik );
    }
}

Güvenlik açısından önemli bir not: sanitize_text_field() kullanmak zorundasınız. Kullanıcıdan gelen her veriyi temizlemeden veritabanına yazmak ciddi güvenlik açıkları doğurur.

Admin Panelinde Göstermek

Veriyi kaydettik ama admin panelinde sipariş detayında görünmüyor. Bu eksikliği tamamlamak için:

// Admin sipariş sayfasında billing alanının altında göster
add_action( 'woocommerce_admin_order_data_after_billing_address', 'admin_tc_kimlik_goster', 10, 1 );

function admin_tc_kimlik_goster( $order ) {
    $tc_kimlik = get_post_meta( $order->get_id(), '_billing_tc_kimlik', true );
    
    if ( ! empty( $tc_kimlik ) ) {
        echo '<p><strong>TC Kimlik Numarası:</strong> ' . esc_html( $tc_kimlik ) . '</p>';
    }
}

Artık sipariş detay sayfasında fatura adresi bilgilerinin hemen altında TC kimlik numarası görünür. Müşteri hizmetleri ekibi bu bilgiye kolayca erişebilir.

Doğrulama (Validation) Eklemek

TC kimlik numarası 11 haneli olmalı. Bunu sunucu tarafında doğrulamak için woocommerce_checkout_process hook’unu kullanıyoruz:

add_action( 'woocommerce_checkout_process', 'tc_kimlik_dogrula' );

function tc_kimlik_dogrula() {
    // Alan doluysa format kontrolü yap
    if ( ! empty( $_POST['billing_tc_kimlik'] ) ) {
        $tc = sanitize_text_field( $_POST['billing_tc_kimlik'] );
        
        // Sadece rakam ve 11 hane kontrolü
        if ( ! preg_match( '/^[0-9]{11}$/', $tc ) ) {
            wc_add_notice( 
                'TC Kimlik Numarası 11 haneli olmalı ve sadece rakam içermelidir.', 
                'error' 
            );
        }
        
        // İlk hane 0 olamaz
        if ( $tc[0] === '0' ) {
            wc_add_notice( 
                'TC Kimlik Numarası 0 ile başlayamaz.', 
                'error' 
            );
        }
    }
}

wc_add_notice() fonksiyonu WooCommerce’in kendi hata gösterim sistemini kullanır. Bu sayede hata mesajları sitenizin temasıyla uyumlu şekilde görünür ve sayfanın üstünde belirgin bir uyarı olarak gösterilir.

Select (Açılır Liste) Alan Eklemek

Bazı durumlarda serbest metin yerine belirli seçenekler sunmak daha mantıklı. Örneğin kurumsal müşterilere “nasıl duydunuz” sorusunu sormak veya teslimat zaman dilimi seçtirmek gibi.

add_filter( 'woocommerce_checkout_fields', 'teslimat_zaman_dilimi_ekle' );

function teslimat_zaman_dilimi_ekle( $fields ) {
    $fields['order']['teslimat_zaman_dilimi'] = array(
        'type'     => 'select',
        'label'    => 'Tercih Ettiğiniz Teslimat Saati',
        'required' => false,
        'class'    => array( 'form-row-wide' ),
        'options'  => array(
            ''        => 'Fark Etmez',
            '09-12'   => 'Sabah (09:00 - 12:00)',
            '12-17'   => 'Öğleden Sonra (12:00 - 17:00)',
            '17-21'   => 'Akşam (17:00 - 21:00)',
        ),
        'priority' => 5,
    );
    return $fields;
}

// Veriyi kaydet
add_action( 'woocommerce_checkout_order_processed', 'teslimat_zaman_dilimi_kaydet', 10, 1 );

function teslimat_zaman_dilimi_kaydet( $order_id ) {
    if ( ! empty( $_POST['teslimat_zaman_dilimi'] ) ) {
        $zaman_dilimi = sanitize_text_field( $_POST['teslimat_zaman_dilimi'] );
        update_post_meta( $order_id, '_teslimat_zaman_dilimi', $zaman_dilimi );
    }
}

Burada dikkat edilecek nokta, alanı billing veya shipping yerine order bölümüne ekledik. Bu şekilde alan, “Siparişiniz Hakkında Notlar” alanının yakınına konumlanır ve mantıksal olarak daha doğru bir yerde durur.

Checkbox Alan Eklemek: KVKK Onayı Örneği

KVKK uyumluluğu için pazarlama onayı checkbox’ı oldukça yaygın bir gereksinim haline geldi:

add_filter( 'woocommerce_checkout_fields', 'pazarlama_onay_checkbox_ekle' );

function pazarlama_onay_checkbox_ekle( $fields ) {
    $fields['order']['pazarlama_onay'] = array(
        'type'     => 'checkbox',
        'label'    => 'Kampanya ve fırsatlardan haberdar olmak istiyorum. (İsteğe bağlı)',
        'required' => false,
        'class'    => array( 'form-row-wide' ),
        'priority' => 30,
    );
    return $fields;
}

add_action( 'woocommerce_checkout_order_processed', 'pazarlama_onay_kaydet', 10, 1 );

function pazarlama_onay_kaydet( $order_id ) {
    // Checkbox işaretliyse 'yes', değilse 'no' kaydet
    $onay = isset( $_POST['pazarlama_onay'] ) ? 'yes' : 'no';
    update_post_meta( $order_id, '_pazarlama_onay', $onay );
    
    // İsteğe bağlı: Kullanıcı meta olarak da kaydet
    $order = wc_get_order( $order_id );
    $user_id = $order->get_user_id();
    
    if ( $user_id > 0 ) {
        update_user_meta( $user_id, 'pazarlama_onay', $onay );
    }
}

Checkbox için özel bir durum var: Checkbox işaretli değilse $_POST dizisinde o key hiç bulunmaz. Bu yüzden isset() kontrolü yaparak hem 'yes' hem 'no' değerini kaydetmek çok daha iyi bir pratik.

Textarea Alan: Kurumsal Müşteri Notu

Kurumsal siparişlerde fatura üzerine yazılacak özel notlar veya proje kodu gibi bilgiler gerekebilir:

add_filter( 'woocommerce_checkout_fields', 'kurumsal_proje_kodu_ekle' );

function kurumsal_proje_kodu_ekle( $fields ) {
    // Sadece kurumsal müşterilere göster mantığı sonra eklenecek
    $fields['billing']['billing_proje_kodu'] = array(
        'type'        => 'textarea',
        'label'       => 'Proje Kodu / Fatura Notu',
        'placeholder' => 'Fatura üzerine işlenmesini istediğiniz bilgileri giriniz...',
        'required'    => false,
        'class'       => array( 'form-row-wide' ),
        'custom_attributes' => array(
            'rows' => 4,
            'cols' => 5,
        ),
        'priority' => 120,
    );
    return $fields;
}

add_action( 'woocommerce_checkout_order_processed', 'kurumsal_proje_kodu_kaydet', 10, 1 );

function kurumsal_proje_kodu_kaydet( $order_id ) {
    if ( ! empty( $_POST['billing_proje_kodu'] ) ) {
        // Textarea için sanitize_textarea_field kullan
        $proje_kodu = sanitize_textarea_field( $_POST['billing_proje_kodu'] );
        update_post_meta( $order_id, '_billing_proje_kodu', $proje_kodu );
    }
}

Textarea için sanitize_text_field() yerine sanitize_textarea_field() kullanmak önemli. İkincisi satır sonlarını (newline karakterlerini) korurken gereksiz HTML taglarını temizler.

Koşullu Alan Gösterimi: Sadece Belirli Ürünler İçin

Bazen özel alanın her siparişte değil, yalnızca belirli kategorideki ürünler sepette olduğunda görünmesini istersiniz. Örneğin “kişiselleştirilmiş ürün” kategorisindeki ürünler için özel mesaj alanı:

add_action( 'woocommerce_checkout_after_customer_details', 'kisisellestirilmis_mesaj_alani' );

function kisisellestirilmis_mesaj_alani() {
    $kategori_slug = 'kisisellestirilmis'; // kategori slug'ınız
    $kisisel_urun_var = false;
    
    foreach ( WC()->cart->get_cart() as $cart_item ) {
        $product_id = $cart_item['product_id'];
        if ( has_term( $kategori_slug, 'product_cat', $product_id ) ) {
            $kisisel_urun_var = true;
            break;
        }
    }
    
    if ( ! $kisisel_urun_var ) {
        return; // Kişisel ürün yoksa hiç gösterme
    }
    
    // WooCommerce form field HTML'i oluştur
    echo '<div id="kisisel_mesaj_wrapper">';
    echo '<h3>Kişisel Mesajınız</h3>';
    
    woocommerce_form_field( 'kisisel_mesaj', array(
        'type'        => 'textarea',
        'label'       => 'Ürüne Eklenecek Kişisel Mesaj',
        'placeholder' => 'Hediye kartına yazılmasını istediğiniz mesajı giriniz...',
        'required'    => false,
        'class'       => array( 'form-row-wide' ),
    ), WC()->checkout->get_value( 'kisisel_mesaj' ) );
    
    echo '</div>';
}

add_action( 'woocommerce_checkout_order_processed', 'kisisel_mesaj_kaydet', 10, 1 );

function kisisel_mesaj_kaydet( $order_id ) {
    if ( ! empty( $_POST['kisisel_mesaj'] ) ) {
        $mesaj = sanitize_textarea_field( $_POST['kisisel_mesaj'] );
        update_post_meta( $order_id, '_kisisel_mesaj', $mesaj );
    }
}

Bu yaklaşım hem kullanıcı deneyimi açısından (alakasız alanları göstermiyoruz) hem de performans açısından oldukça temiz.

Sipariş Onay E-postasına Özel Alan Eklemek

Müşteriye gönderilen sipariş onay e-postasında da bu özel alanları gösterebilirsiniz:

add_action( 'woocommerce_email_order_meta', 'email_ozel_alanlar_goster', 10, 3 );

function email_ozel_alanlar_goster( $order, $sent_to_admin, $plain_text ) {
    $tc_kimlik = get_post_meta( $order->get_id(), '_billing_tc_kimlik', true );
    $zaman_dilimi = get_post_meta( $order->get_id(), '_teslimat_zaman_dilimi', true );
    $kisisel_mesaj = get_post_meta( $order->get_id(), '_kisisel_mesaj', true );
    
    if ( $plain_text ) {
        // Düz metin e-posta formatı
        if ( $tc_kimlik ) {
            echo "TC Kimlik: " . $tc_kimlik . "n";
        }
        if ( $zaman_dilimi ) {
            echo "Teslimat Saati Tercihi: " . $zaman_dilimi . "n";
        }
    } else {
        // HTML e-posta formatı
        if ( $tc_kimlik ) {
            echo '<p><strong>TC Kimlik Numarası:</strong> ' . esc_html( $tc_kimlik ) . '</p>';
        }
        if ( $zaman_dilimi ) {
            echo '<p><strong>Tercih Edilen Teslimat Saati:</strong> ' . esc_html( $zaman_dilimi ) . '</p>';
        }
        if ( $kisisel_mesaj ) {
            echo '<p><strong>Kişisel Mesaj:</strong> ' . nl2br( esc_html( $kisisel_mesaj ) ) . '</p>';
        }
    }
}

$sent_to_admin parametresiyle admin e-postası ile müşteri e-postasını ayırt edebilirsiniz. Bazı bilgileri (örneğin iç notlar) sadece admin’e gönderip müşteriye göndermemek isteyebilirsiniz.

Yaygın Sorunlar ve Çözümleri

Production ortamında bu kodları uygularken karşılaşabileceğiniz sorunlar şunlar:

  • Alan kaydedilmiyor: woocommerce_checkout_order_processed yerine woocommerce_checkout_update_order_meta hook’unu deneyebilirsiniz. WooCommerce versiyonuna göre davranış farklılık gösterebilir.
  • Alan görünmüyor ama hata da yok: priority değerini kontrol edin. Eğer eklediğiniz alan başka bir alan veya blok tarafından örtülüyorsa farklı bir priority deneyin.
  • Tema çakışması: Bazı temalar (özellikle Divi veya Avada gibi page builder odaklı temalar) checkout sayfasını tamamen override ediyor olabilir. Bu durumda temanın özel checkout template’ini bulmak gerekir.
  • Sayfa önbelleği sorunu: WP Rocket veya benzeri önbellek eklentisi kullanıyorsanız checkout sayfasını cache’den hariç tuttuğunuzdan emin olun.
  • Alan değeri kayboldu: Eğer AJAX ile güncellenen bir checkout kullanıyorsanız woocommerce_checkout_update_order_review hook’unu da dinlemek gerekebilir.

Mevcut Alanları Düzenlemek

Sadece yeni alan eklemekle kalmayıp mevcut alanları da değiştirebilirsiniz:

add_filter( 'woocommerce_checkout_fields', 'mevcut_alanlar_duzenle' );

function mevcut_alanlar_duzenle( $fields ) {
    // Telefon alanını zorunlu yap
    $fields['billing']['billing_phone']['required'] = true;
    
    // Şirket adı alanının label'ını değiştir
    $fields['billing']['billing_company']['label'] = 'Firma / Kurum Adı';
    
    // Adres 2 alanını kaldır (çoğu site için gereksiz)
    unset( $fields['billing']['billing_address_2'] );
    unset( $fields['shipping']['shipping_address_2'] );
    
    // Fatura adı alanlarına placeholder ekle
    $fields['billing']['billing_first_name']['placeholder'] = 'Adınız';
    $fields['billing']['billing_last_name']['placeholder'] = 'Soyadınız';
    
    return $fields;
}

Bu kod gerçek dünya projelerinde çok işe yarar. “Adres 2” alanını kaldırmak formun görsel karmaşıklığını azaltır ve dönüşüm oranlarını olumlu etkiler.

Verileri WooCommerce 3.x+ Uyumlu Kaydetmek

WooCommerce 3.0 ile birlikte update_post_meta() yerine order nesnesinin kendi metodlarını kullanmak daha doğru bir yaklaşım:

add_action( 'woocommerce_checkout_order_processed', 'modern_veri_kaydet', 10, 3 );

function modern_veri_kaydet( $order_id, $posted_data, $order ) {
    if ( ! empty( $_POST['billing_tc_kimlik'] ) ) {
        $tc_kimlik = sanitize_text_field( $_POST['billing_tc_kimlik'] );
        
        // WC 3.x+ uyumlu yöntem
        $order->update_meta_data( '_billing_tc_kimlik', $tc_kimlik );
        $order->save();
    }
}

update_post_meta() hala çalışır ama WooCommerce’in kendi API’sini kullanmak gelecek versiyonlarla uyumu garantiler ve HPOS (High Performance Order Storage) desteği için zorunlu hale gelebilir.

Güvenlik Kontrol Listesi

Bu kodları canlı ortama taşımadan önce kontrol etmeniz gereken güvenlik noktaları:

  • Nonce doğrulaması: WooCommerce checkout formu zaten nonce kullanıyor ama custom AJAX istekleri yazıyorsanız kendi nonce’unuzu ekleyin
  • Sanitizasyon: Her $_POST değeri için uygun sanitizasyon fonksiyonunu kullanın. Metin için sanitize_text_field(), textarea için sanitize_textarea_field(), e-posta için sanitize_email(), tam sayı için absint()
  • Escaping: Veriyi ekranda gösterirken her zaman esc_html() veya esc_attr() kullanın
  • Capability kontrolü: Admin sayfalarında veri gösterirken current_user_can() ile yetki kontrolü yapın
  • SQL injection: get_post_meta() ve WooCommerce API kullanıyorsanız SQL injection riski zaten minimize edilmiş ama doğrudan $wpdb kullanıyorsanız prepare() zorunlu

Sonuç

WooCommerce ödeme sayfasına özel alan eklemek, başta karmaşık görünse de doğru hook’ları öğrendiğinizde oldukça sistematik bir süreç. Temel akış her zaman şu: alanı tanımla, göster, doğrula, kaydet ve admin panelinde görüntüle.

Gerçek projelerde bu adımları bir plugin yapısında organize etmek çok daha sürdürülebilir. functions.php prototipler ve küçük özelleştirmeler için ideal ama projeniz büyüdükçe tüm checkout özelleştirmelerini kendi eklentinizde toplamak bakımı kolaylaştırır.

Son olarak, her WooCommerce güncellemesinden sonra özelleştirilmiş checkout sayfasını test etmeyi alışkanlık haline getirin. WooCommerce core ekibi özellikle checkout akışını aktif geliştiriyor ve hook davranışları zaman zaman değişebiliyor. Bir staging ortamında önce test etmek, production’da sürprizlerle karşılaşmanızı önler.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir