Müsait — Yerel Dijital Ürünler
HCA · Studio
TR İletişim ↗
Teknik Not 01 · WooCommerce

WooCommerce'e yemek kartı ödemesi eklemek

Türkiye'de online yemek siparişi alan bir sitenin yemek kartı kabul etmemesi, hedef kitlesinin önemli bir bölümünü ödeme adımında kaybetmesi demek. WooCommerce bu kartları hazır desteklemez; çözüm özel bir ödeme geçidi yazmaktır.

Konu
Ödeme geçidi geliştirme
Platform
WooCommerce
Seviye
Orta — ileri
Kısa cevap

WooCommerce'te Multinet, Sodexo veya Setcard gibi yemek kartlarını kabul etmek için hazır bir eklenti yoktur; sağlayıcının entegrasyon dokümanına göre WC_Payment_Gateway sınıfını genişleten özel bir ödeme geçidi yazmak gerekir. Kritik nokta, kullanıcının geri döndüğü adresteki parametrelere güvenmemek: ödemenin gerçekleştiği, dönüş adımında sağlayıcıya atılan ayrı bir sunucu-sunucu sorgusuyla teyit edilmelidir. Sipariş yalnızca bu teyitten sonra ödendi olarak işaretlenmelidir.

Neden hazır eklenti yok

WooCommerce'in ödeme ekosistemi büyük ölçüde uluslararası sağlayıcılar (Stripe, PayPal) ve Türkiye'de yaygın sanal POS sağlayıcıları etrafında kurulu. Yemek kartı sağlayıcıları ise farklı bir kategoride: kurumsal sözleşmeye bağlı, üye iş yeri onayı gerektiren ve entegrasyon dokümanı yalnızca anlaşma sonrası paylaşılan sistemler.

Bu yüzden eklenti dizininde "kur ve çalıştır" tarzı bir çözüm bulamazsınız. Bulduğunuzu iddia eden eklentilere de dikkat edin: ödeme akışı, sağlayıcının kendi API sürümüne ve size özel üye iş yeri parametrelerine bağlıdır.

İlk adım teknik değil ticari: sağlayıcıyla üye iş yeri anlaşması yapılır, test ortamı bilgileri ve entegrasyon dokümanı alınır. Bu bilgiler olmadan yazılacak kod tahminden ibarettir.

Ödeme geçidinin iskeleti

WooCommerce'te bir ödeme yöntemi, WC_Payment_Gateway sınıfını genişleten bir sınıftır ve woocommerce_payment_gateways filtresiyle sisteme tanıtılır.

add_filter( 'woocommerce_payment_gateways', function ( $gateways ) {
    $gateways[] = 'WC_Gateway_Meal_Card';
    return $gateways;
} );

class WC_Gateway_Meal_Card extends WC_Payment_Gateway {

    public function __construct() {
        $this->id                 = 'meal_card';
        $this->method_title       = 'Yemek Kartı';
        $this->method_description = 'Yemek kartı ile ödeme.';
        $this->has_fields         = false;

        $this->init_form_fields();
        $this->init_settings();

        $this->title   = $this->get_option( 'title' );
        $this->enabled = $this->get_option( 'enabled' );

        add_action( 'woocommerce_update_options_payment_gateways_' . $this->id,
            array( $this, 'process_admin_options' ) );
    }

    public function process_payment( $order_id ) {
        $order = wc_get_order( $order_id );

        // Sağlayıcıya işlem başlatma isteği gönderilir,
        // dönen yönlendirme adresi kullanıcıya verilir.
        $redirect = $this->start_transaction( $order );

        if ( is_wp_error( $redirect ) ) {
            wc_add_notice( $redirect->get_error_message(), 'error' );
            return array( 'result' => 'failure' );
        }

        return array(
            'result'   => 'success',
            'redirect' => $redirect,
        );
    }
}

Burada dikkat edilecek nokta: process_payment siparişi ödendi olarak işaretlemez. Yalnızca kullanıcıyı sağlayıcının doğrulama ekranına gönderir. Ödemenin gerçekleşip gerçekleşmediği bu aşamada bilinmiyor.

Ödeme akışı

Yemek kartı entegrasyonları tipik olarak yönlendirmeli (redirect) çalışır. Akış şöyledir:

  1. Müşteri ödeme adımında yemek kartını seçer ve siparişi verir.
  2. Site, sağlayıcıya sunucu tarafından bir işlem başlatma isteği gönderir; sipariş tutarı, sipariş referansı ve dönüş adresi bu istekte yer alır.
  3. Sağlayıcı bir işlem kimliği ve yönlendirme adresi döner.
  4. Müşteri sağlayıcının ekranında kart bilgisini/doğrulamasını tamamlar.
  5. Sağlayıcı müşteriyi sitedeki dönüş adresine geri gönderir.
  6. Site, sağlayıcıya ikinci bir sunucu-sunucu sorgusu atarak işlemin gerçek durumunu sorar.
  7. Durum başarılıysa sipariş ödendi olarak işaretlenir ve mutfağa/hazırlığa düşer.

Altıncı adım bu yazının asıl konusu.

En sık yapılan güvenlik hatası

Ödeme entegrasyonlarında gördüğüm en yaygın hata şu: sağlayıcı kullanıcıyı /odeme-donus/?durum=basarili&siparis=1234 gibi bir adrese geri gönderir; kod bu adresteki durum=basarili parametresine bakar ve siparişi ödendi sayar.

Bu adres kullanıcının tarayıcısından geçer. Yani parametreyi elle yazan biri, hiç ödeme yapmadan siparişi ödenmiş duruma getirebilir. Restoran siparişlerinde bunun sonucu doğrudan mal kaybıdır — sipariş mutfağa düşer, hazırlanır, teslim edilir.

Doğru davranış: dönüş adımında yalnızca "kullanıcı geri döndü" bilgisi alınır, gerçek karar sağlayıcıdan sorulur.

public function handle_return() {
    $order_id = absint( $_GET['order_id'] ?? 0 );
    $order    = wc_get_order( $order_id );

    if ( ! $order || $order->is_paid() ) {
        return; // yoksa veya zaten ödenmişse tekrar işleme
    }

    // Karar tarayıcıdan değil, sağlayıcıdan gelir:
    $status = $this->query_transaction_status( $order->get_id() );

    if ( is_wp_error( $status ) || 'approved' !== $status['state'] ) {
        $order->update_status( 'failed', 'Yemek kartı ödemesi onaylanmadı.' );
        wp_safe_redirect( wc_get_checkout_url() );
        exit;
    }

    // Tutar da doğrulanmalı — eksik tutarla onay kabul edilmemeli.
    if ( ! $this->amounts_match( $status['amount'], $order->get_total() ) ) {
        $order->update_status( 'on-hold', 'Tutar uyuşmuyor, elle kontrol gerekiyor.' );
        return;
    }

    $order->payment_complete( $status['transaction_id'] );
    wp_safe_redirect( $this->get_return_url( $order ) );
    exit;
}

Üç koruma birden var: durum sağlayıcıdan sorgulanıyor, tutar karşılaştırılıyor ve zaten ödenmiş sipariş tekrar işlenmiyor. Sonuncusu önemli — kullanıcı dönüş sayfasını yenilediğinde ya da sağlayıcı bildirimi tekrarladığında sipariş iki kez tamamlanmamalı.

Sipariş durumları

WooCommerce sipariş durumlarını doğru kullanmak, muhasebe ve operasyon açısından entegrasyonun yarısıdır:

DurumNe zamanNeden
pendingSipariş oluşturuldu, ödeme başlatıldıHenüz para yok; mutfağa düşmemeli
failedSağlayıcı onay vermedi veya iptal edildiMüşteri tekrar deneyebilir
on-holdTutar uyuşmazlığı gibi belirsiz durumOtomatik karar verilemez, insan bakmalı
processingÖdeme teyit edildipayment_complete() bunu kendi ayarlar

Yaygın bir yanlış, ödemeyi update_status('processing') ile elle işaretlemek. Bunun yerine payment_complete() kullanılmalı: bu metot işlem numarasını kaydeder, stok düşer, ilgili kancaları tetikler ve mağaza türüne göre doğru durumu seçer.

Blok checkout uyumu

WooCommerce'in yeni blok tabanlı ödeme sayfası, klasik kısa kodlu sayfadan farklı çalışır: ödeme yöntemleri React tarafında listelenir. Klasik yöntemle yazılmış bir geçit, blok checkout'ta hiç görünmeyebilir.

Bunun için geçidin blok desteğini ayrıca bildirmesi gerekir:

add_action( 'woocommerce_blocks_loaded', function () {
    if ( ! class_exists( 'Automattic\WooCommerce\Blocks\Payments\Integrations\AbstractPaymentMethodType' ) ) {
        return;
    }
    require_once __DIR__ . '/class-meal-card-blocks.php';

    add_action(
        'woocommerce_blocks_payment_method_type_registration',
        function ( $registry ) {
            $registry->register( new WC_Meal_Card_Blocks_Support() );
        }
    );
} );

Blok tarafı JavaScript ile kayıt olduğu için, geçidin bir de küçük bir JS dosyası olması gerekir. Bu dosya ertelenerek (defer) veya birleştirilerek yüklenmemelidir — aksi hâlde ödeme yöntemi listede hiç belirmez. Bu tuzağın ayrıntısı için "sepet boş" hatası notuna bakın.

Test ve canlıya alma

  • Test ortamıyla başlayın. Sağlayıcının test üye iş yeri bilgileriyle en az şu senaryoları geçin: başarılı ödeme, yetersiz bakiye, kullanıcının vazgeçmesi, dönüş sayfasının yenilenmesi.
  • Yetersiz bakiye senaryosu kritiktir. Yemek kartlarında bakiye çoğu zaman sipariş tutarından düşüktür; bu durumda kullanıcıya ne olduğunu anlatan bir mesaj gösterilmeli, sipariş failed olmalı ve sepet korunmalıdır.
  • Kayıt tutun. Sağlayıcıya giden ve gelen her isteğin özetini WooCommerce sipariş notlarına yazın. Ödeme tartışmalarında tek dayanağınız bu notlar olur.
  • Anahtarları kodda tutmayın. Üye iş yeri anahtarları eklenti ayarlarında veya wp-config.php sabitlerinde saklanmalı; sürüm kontrolüne girmemeli.

Özet

Yemek kartı entegrasyonu, WooCommerce ödeme geçidi yazmanın standart bir uygulamasıdır; zorluk sağlayıcının dokümanını doğru okumakta ve güven sınırını doğru yere koymakta. Kullanıcının tarayıcısından gelen hiçbir bilgi ödeme kanıtı değildir. Sipariş yalnızca sunucudan sunucuya doğrulanmış bir onaydan sonra ödendi sayılmalıdır.

Bu yaklaşım, Her Mutfak sipariş sisteminde canlı olarak uygulanmıştır.

Kontrol listesi
Ön koşul
Sağlayıcıyla üye iş yeri anlaşması + entegrasyon dokümanı
Sınıf
WC_Payment_Gateway genişletilir, woocommerce_payment_gateways ile kaydedilir
Güven sınırı
Dönüş adresindeki parametreye ASLA güvenilmez — durum sağlayıcıdan sorgulanır
Tutar kontrolü
Onaylanan tutar sipariş tutarıyla karşılaştırılır
Tekrar koruması
Zaten ödenmiş sipariş yeniden işlenmez
Tamamlama
update_status yerine payment_complete() kullanılır
Blok checkout
Ayrı blok kaydı gerekir; JS dosyası defer edilmemeli
Sırlar
Anahtarlar ayarlarda veya wp-config sabitlerinde, kodda değil
Sık Sorulan Sorular

Yemek kartı entegrasyonu hakkında.

WooCommerce'e Multinet ödemesi eklemek için hazır eklenti var mı?

Pratikte yok. Yemek kartı sağlayıcıları entegrasyon bilgilerini yalnızca üye iş yeri anlaşması yapan işletmelerle paylaşır ve parametreler işletmeye özeldir. Bu nedenle çözüm, sağlayıcının dokümanına göre WC_Payment_Gateway sınıfını genişleten özel bir ödeme geçidi yazmaktır.

Ödeme sonrası dönüş adresindeki parametrelere neden güvenilmez?

Çünkü o adres kullanıcının tarayıcısından geçer ve içeriği değiştirilebilir. Yalnızca URL'deki 'başarılı' bilgisine bakan bir sistem, hiç ödeme yapmadan sipariş oluşturulmasına izin verir. Doğru yöntem, dönüş adımında sağlayıcıya ayrı bir sunucu-sunucu sorgusu atıp işlem durumunu ve tutarı oradan doğrulamaktır.

Ödeme yöntemim blok checkout'ta görünmüyor, neden?

Blok tabanlı ödeme sayfası, ödeme yöntemlerini JavaScript tarafında listeler. Klasik yönteme göre yazılmış bir geçidin ayrıca blok desteğini kaydetmesi gerekir. Ayrıca geçidin blok JS dosyası ertelenerek veya birleştirilerek yüklendiğinde yöntem listede hiç belirmez.

Sipariş durumunu processing yapmak yerine neden payment_complete kullanmalıyım?

payment_complete() yalnızca durumu değiştirmez: işlem numarasını kaydeder, stoğu düşer, ilgili kancaları tetikler ve mağaza türüne göre doğru sipariş durumunu seçer. Durumu elle ayarlamak bu adımları atlar ve raporlama ile stok tarafında sessiz hatalar bırakır.

Teklif ve Uygunluk

Yerel aramada görünür.
Üründe inandırıcı.