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.
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.
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.
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.
Yemek kartı entegrasyonları tipik olarak yönlendirmeli (redirect) çalışır. Akış şöyledir:
Altıncı adım bu yazının asıl konusu.
Ö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ı.
WooCommerce sipariş durumlarını doğru kullanmak, muhasebe ve operasyon açısından entegrasyonun yarısıdır:
| Durum | Ne zaman | Neden |
|---|---|---|
pending | Sipariş oluşturuldu, ödeme başlatıldı | Henüz para yok; mutfağa düşmemeli |
failed | Sağlayıcı onay vermedi veya iptal edildi | Müşteri tekrar deneyebilir |
on-hold | Tutar uyuşmazlığı gibi belirsiz durum | Otomatik karar verilemez, insan bakmalı |
processing | Ödeme teyit edildi | payment_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.
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.
failed olmalı ve sepet korunmalıdır.wp-config.php sabitlerinde saklanmalı; sürüm kontrolüne girmemeli.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.
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.
Çü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.
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.
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.