WooCommerce'te teklif isteme akışı nasıl kurulur
Fiyatı gizlemek işin kolay yarısı. Asıl mesele talebi kaydedecek, fiyatlandıracak ve ödemeye bağlayacak bir döngü kurmak; bu yazı teklifin neden bir eklentinin özel tablosunda değil, yerel bir WooCommerce siparişi olarak durması gerektiğini anlatıyor.
Tek bir sabit fiyatı olmayan ürünler satıyorsunuz. Fiyat; hacme, müşterinin sözleşmesine, belirli bir posta koduna palet göndermeye ya da yanıt verebilmek için önce okumanız gereken bir şartnameye bağlı olabilir. Dolayısıyla Satın Al düğmesi yanlış bir seçim ve onun yerine bir WooCommerce teklif isteme akışı istiyorsunuz: müşteri ne istediğini söyler, siz bir rakam çıkarırsınız, o da öder.
Çoğu mağaza yolun yarısını gelir. Bir sayfaya iletişim formu koyarlar, talepler e-posta olarak düşer, sonra da her şey takip aşamasında dağılır. Müşterinin hangi SKU’yu kastettiğini kimse hatırlamaz, verilen fiyat giden kutusunda kalır ve müşteri evet dediğinde ödenecek bir sipariş yoktur. Bu yazı döngünün tamamını baştan sona anlatıyor, yaygın yaklaşımların bağlamı nerede sızdırdığını açıklıyor ve bir teklifi neden bir eklentinin özel tablosundaki satır olarak değil de yerel bir WooCommerce siparişi olarak saklamanın daha doğru olduğunu gösteriyor.
WooCommerce teklif isteme akışının gerçekte neye ihtiyacı var#
Araç seçmeden önce sürecin biçimi konusunda dürüst olun. Çalışan bir teklif döngüsünün beş adımı vardır ve bunlardan birini atlamak başka bir yerde elle iş yaratır:
- Fiyatı bastırın. Ürün sayfası hâlâ £0.00 ya da eskimiş bir rakam gösteriyorsa müşteri kendini o rakama göre ayarlar ve verdiğiniz teklif zam gibi görünür. Bu iş sunucu tarafında, fiyatın basıldığı her yerde yapılmalıdır.
- Eylemi değiştirin. Sepete ekle başka bir şeye dönüşmeli: ürün kimliğini yanında taşıyan bir talep düğmesine. Böylece “hangi modele bakıyordunuz?” diye sormak zorunda kalmazsınız.
- Talebi üzerinde çalışabileceğiniz bir yerde toplayın. Durumu, sahibi, zaman damgası ve not yazacak yeri olan bir yer. Gelen kutusu böyle bir yer değildir.
- Fiyatla yanıt verin. İdeali satır kalemi bazında olması ve eski bir teklifin altı ay sonra karşınıza çıkmaması için bir geçerlilik tarihi taşıması.
- Kabul edip ödeme yapmalarına imkân verin. Herkesin unuttuğu adım. Kabul etmek, müşterinin “olur, gönderin” diye e-posta atması ve sizin ardından siparişi elle oluşturmanız demekse, işin yalnızca kolay yarısını otomatikleştirmişsiniz demektir.
Birinci ve ikinci adımlar saf WooCommerce işidir; bugün bir kod parçasıyla halledebilirsiniz. Tasarım kararının bulunduğu yer üçüncü adımdan beşinciye kadar olan kısımdır.
Birinci adım: fiyatı gizleyin ve sepete ekleyi kaldırın#
WooCommerce fiyatları az sayıda filtre üzerinden, sepete ekleyi ise şablon kancaları üzerinden basar. Klasik bir temada mağaza genelinde bir teklif akışı için bu gerçekten birkaç satırlık iştir:
add_filter( 'woocommerce_get_price_html', function ( $html, $product ) {
return '<span class="price-on-request">Price on request</span>';
}, 100, 2 );
add_filter( 'woocommerce_is_purchasable', '__return_false' );
remove_action( 'woocommerce_after_shop_loop_item', 'woocommerce_template_loop_add_to_cart', 10 );
remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30 );Bu kod parçası hakkında iki not, çünkü işin tamamı bu değil. Önemli satır woocommerce_is_purchasable: WC_Cart::add_to_cart() çağrısının ?add-to-cart=123 biçiminde elle hazırlanmış bir isteği reddetmesini sağlayan odur; şablon eylemini kaldırmak ise düğmeyi yalnızca sayfadan alır. Blok temalarda ise remove_action çağrıları hiçbir işe yaramaz, çünkü bloklaştırılmış şablonlar sepete ekleyi woocommerce_after_shop_loop_item üzerinden değil kendi blokları üzerinden basar. Fiyat filtresi insanların beklediğinden biraz daha iyi durumdadır: Store API’sinin price_html alanını get_price_html() ürettiği için yazdığınız metin orada da görünür. Ama aynı yanıt ham bir prices nesnesi de taşır; içinde price, regular_price ve sale_price alt birim cinsinden yer alır ve hiçbir fiyat HTML’i filtresi bu sayılara dokunmaz.
“Gizlenmiş” bir fiyatın hâlâ okunabilir olmasının açık ara en yaygın nedeni bu boşluktur. Bu işin katalog modu tarafını eklentisiz WooCommerce katalog modu yazısında, fiyat tarafını ise Sepete Ekle düğmesini gizleme yazısında daha uzun uzun ele aldık. Tam bir teklif süreci işletmek yerine yalnızca fiyatları girişin arkasına almanız yetiyorsa, müşteriler giriş yapana kadar fiyatları gizlemek buradan sonrasına göre çok daha küçük bir iştir.
İkinci adım: talebi nasıl toplayacağınızı seçmek#
Mağazaların bir WooCommerce teklif isteme sürecini yürütmek için gerçekten kullandığı üç yaklaşım şunlar. Ölçüt de asıl önemli olan şey: müşterinin tıklamasından paranın el değiştirdiği ana kadar bağlamın ne kadarı hayatta kalıyor.
| Yaklaşım | Size maliyeti | Ne zaman makul |
|---|---|---|
| Düz iletişim formu (CF7, Gravity, Fluent) | Ürün kimliği serbest metindir. Durum yok, satır kalemi yok, fiyat alanı yok, ödemeye giden yol yok. Kabul edilen her teklif elle kurulan bir siparişe dönüşür. | Ayda iki üç talep alıyorsanız ve kataloğunuzu ezbere biliyorsanız. |
| Elle yürütülen e-posta yazışması | Yukarıdakilerin tamamı, üstüne kayıt tek bir kişinin posta kutusunda yaşar. Devir yok, raporlama yok, “mart ayında bunlara ne fiyat vermiştik?” sorusuna yanıt verecek bir yol yok. | İlk aydan sonrası için asla. En hızlısı gibi hissettirir, en kötü yaşlanan odur. |
| Teklif eklentisi | Tamamen talebi nerede sakladığına bağlıdır. Kendi özel tablosunu kullanan bir eklenti size ikinci ve daha zayıf bir yönetim ekranı verir. Yerel sipariş yazan bir eklenti ise WooCommerce’in bütün araç setini verir. | Teklif vermenin ara sıra yapılan bir jest değil, tekrar eden bir süreç olduğu her mağazada. |
İletişim formu seçeneği adil bir değerlendirmeyi hak ediyor, çünkü ücretsiz ve işini görüyor. Form eklentiniz gizli bir alanı önceden doldurmanıza izin veriyorsa, ürün kimliğini düğmeden bu alana geçirip kimlik sorununun büyük bölümünü çözebilirsiniz:
add_action( 'woocommerce_single_product_summary', function () {
global $product;
printf(
'<a class="button" href="%s">Request a quote</a>',
esc_url( add_query_arg( 'product', $product->get_id(), site_url( '/quote-request/' ) ) )
);
}, 30 );Hedef sayfada bu product parametresini okuyun, adını bulun ve gizli bir alana yerleştirin. Artık SKU’yu adıyla belirten bir form gönderiminiz var. Hâlâ sahip olmadığınız şeyler ise şunlar: üzerinde filtreleyebileceğiniz bir durum, verdiğiniz fiyatı kaydedecek bir yer ve bir ödeme bağlantısı. Hacim büyüdükçe e-tablo işine dönüşen kısım da tam olarak burasıdır.
Bir teklif neden siparişler tablosuna aittir#
Tasarım savı budur ve hangi eklentiyi seçerseniz seçin geçerlidir. Teklif ile sipariş, ömrünün farklı aşamalarındaki aynı nesnedir. İkisinin de bir müşterisi, adresi, adetleriyle birlikte satır kalemleri, toplamı, kimin ne yaptığının geçmişi ve bir durum makinesi vardır. WooCommerce bunların hepsini test edilmiş, çevrilmiş ve kurulu olan diğer her şeyle bütünleşmiş halde zaten sunuyor.
Bir teklifi özel durumu olan bir WooCommerce siparişi olarak saklarsanız şunları bedavaya devralırsınız:
- Çalışan bir arama. Siparişler ekranı müşteri adında, e-postada, adreste ve ürün adlarında arama yapar. Özel bir tablo ise ancak yazarının yazmaya vakit bulduğu arama kutusuna sahip olur.
- Sipariş notları. Zaman damgalı, kime ait olduğu belli, özel ya da müşteriye açık. Anlaşılan şey tartışmaya düştüğünde denetim iziniz budur; sizin başlattığınız bir teklifi bir iş arkadaşınızın devralabilmesinin nedeni de budur.
- E-posta sistemi. Şablonlar, WooCommerce üst ve alt bilgisi, önizleme aracı ve halihazırda yapılandırdığınız hangi işlemsel posta servisi ise o.
- HPOS uyumluluğu. High-Performance Order Storage, modern WooCommerce’in kullandığı dizinli siparişler tablosudur. Orada yaşayan bir nesne sipariş hacminizle birlikte ölçeklenir ve Woo’nun kendi taşıma araçlarının kapsamına girer.
- Ödeme adımından ibaret bir kabul yolu. Müşteri evet dediğinde sipariş, doğru satırları ve doğru toplamlarıyla zaten hazırdır. Ödeme bekliyor durumuna geçer ve normal ödeme geçidinizden akar. Hiçbir şey yeniden girilmez.
Bir tuzak var ve bunu açıkça söylemekte fayda var, çünkü acemi uygulamaların yanlış yaptığı yer tam olarak burasıdır. Özel bir sipariş durumu kendiliğinden rakamlarınızın dışında kalmaz. WooCommerce Analytics, siz hariç tutmadıkça her durumu raporlar; dolayısıyla yanıtlanmamış tekliflerden oluşan bir yığın, kararlarınızı dayandırdığınız ciro grafiğini sessizce şişirir. Durumu kaydetmek işin kolay yarısıdır:
register_post_status( 'wc-quote-request', array(
'label' => 'Quote Request',
'public' => false,
'show_in_admin_status_list' => true,
) );
add_filter( 'wc_order_statuses', function ( $statuses ) {
$statuses['wc-quote-request'] = 'Quote Request';
return $statuses;
} );Diğer yarısı hariç tutmadır ve bu bir kanca değil, bir ayardır: WooCommerce > Ayarlar > Analytics bölümünde yeni durumu Hariç tutulan durumlar listesine ekleyin ki raporlar onu yok saysın. woocommerce_order_is_paid_statuses filtresine dokunmanıza gerek yok; bu filtre varsayılan olarak processing ve completed döndürür, yani özel bir durum ancak siz oraya kendiniz eklerseniz ödenmiş sayılır. Yalnızca kendi kodunuzda bunu yapan bir şey olmadığından emin olun. PriceVeil tam olarak bu davranışı uygular: teklif talepleri, Quote Request durumuna sahip yerel WooCommerce siparişlerine dönüşür, HPOS uyumludur, Siparişler listesinde kendi filtre bağlantısı bulunur ve ciro dürüst kalsın diye hem ödenmiş durumların hem de raporların dışında açıkça tutulur.
Üçüncü adım: fiyatla yanıt vermek ve ödemeyi almak#
Talep bir siparişe dönüştükten sonra yanıt vermek, büyük ölçüde ne kadar yapı istediğinizle ilgili bir sorudur. En basit haliyle sipariş ekranında satır kalemi fiyatlarını düzenler ve müşteriye bir ödeme bağlantısı gönderirsiniz. WooCommerce bu bağlantıyı bekleyen siparişler için sipariş anahtarına dayalı olarak kendisi üretir ve bağlantı hesap açılmadan da çalışır.
Bu meşru ve ücretsiz bir iş akışıdır; pek çok mağaza burada durur. Size vermediği şey ise müşterilerin asıl karşılık verdiği kısımdır: teklife benzeyen bir belge, bir geçerlilik süresi, işaretleyebilecekleri isteğe bağlı ek kalemler ve göze batan bir kabul ya da ret. PriceVeil Pro bu katmanı ekliyor: New / Quote sent / Accepted / Declined sekmeleri olan bir talep konsolu. Burada her satırı fiyatlandırır, sayfayı yeniden yüklemeden başka ürünler eklersiniz, satırları zorunlu, isteğe bağlı veya alternatif olarak işaretlersiniz, 7, 14 ya da 30 günlük bir geçerlilik veya sabit bir tarih belirlersiniz, sonra gönderirsiniz. Müşteri, sipariş anahtarıyla doğrulanan bir sayfa alır: seçenekleri işaretledikçe güncellenen canlı bir toplam, geri sayan bir geçerlilik süresi ve siparişi yeniden kurup müşteriyi normal WooCommerce ödeme adımına bırakan bir Kabul et ve öde düğmesi. Pro, teklifi WhatsApp üzerinden de iletebilir; bunun için API gerektirmeyen bir sohbete-tıkla bağlantısı ve varsayılan olarak kaydettiğiniz bir mesaj şablonu kullanır. Alıcıların sohbet mesajına yanıt verip e-postayı görmezden geldiği pazarlarda bu işe yarar.
Ne kurarsanız kurun, teklife bir geçerlilik süresi koyun. Malzeme maliyetleri oynar ve dokuz ay önceki teklifi çıkarıp önünüze koyan bir müşteriyle giriştiğiniz tartışmayı kaybedersiniz.
Herkese açık bir teklif formu için spam koruması#
Her gönderimde veritabanına kayıt açan bir form hedeftir. Onu ilk günden itibaren öyle görün, çünkü çöp siparişleri sipariş tablonuzdan sonradan temizlemek, onların oluşmasını engellemekten çok daha zahmetlidir. Üç ucuz katman neredeyse her şeyi durdurur:
- Bir nonce. Gönderimi bir oturuma ve formun basıldığı ana bağlar. Uç noktanıza yapılan en kaba doğrudan POST isteklerini durdurur.
- Bir honeypot. CSS ile gizlenmiş, insanın hiç görmediği bir alan. Otomatik gönderim yapanlar buldukları her girdiyi doldurur; alanda değer varsa talebi sessizce düşürün. Sessizce olması önemlidir, çünkü hata mesajı bota yeniden denemeyi öğretir.
- IP başına hız sınırı. Asıl işi yapan katman budur. Karması alınmış IP’ye göre anahtarlanan ve saatte bir avuç gönderime izin veren bir transient, diğer ikisini aşan her şeyin verebileceği zararı sınırlar.
$key = 'quote_rl_' . md5( $_SERVER['REMOTE_ADDR'] ?? '' );
$hits = (int) get_transient( $key );
if ( $hits >= 5 ) {
wp_send_json_error( array( 'message' => 'Too many requests. Try again later.' ), 429 );
}
set_transient( $key, $hits + 1, HOUR_IN_SECONDS );Bir ters vekil sunucunun ya da Cloudflare’in arkasında REMOTE_ADDR değerinin ziyaretçiyi değil vekil sunucuyu gösterdiğini unutmayın. Bu durumda barındırma sağlayıcınızın ayarladığı iletilen başlığı okumanız gerekir ve bu başlığa yalnızca sağlayıcınız onu gerçekten ayarlıyorsa güvenin. PriceVeil’in yerleşik teklif formu, [priceveil_form] kısa kodunda ve bloğunda bu üç katmanın hepsini birden sunar. İster onu kullanın ister kendinizinkini yazın, hız sınırı olmayan herkese açık bir formu yayına almayın.
İlk iş olarak CAPTCHA’ya sarılma dürtüsüne direnin. Yukarıdaki üç katmanın zaten hallettiği bir sorunu durdurmak için her meşru B2B alıcısına bedel ödetir; bir teklif formunda ise dönüşüm oranınız işin ta kendisidir.
Bu yaklaşımın çözmediği şeyler#
Bir teklif akışı, vitrinin ne gösterdiğini ve müşteri talep bıraktıktan sonra ne olacağını değiştirir. WooCommerce’in dışına uzanmaz ve kendi sitenizde kontrol etmeye değer dört sızıntı vardır.
- Ürün akışları. CTX Feed, Google Product Feed ya da bir satıcı entegrasyonu kullanıyorsanız bunlar fiyatları doğrudan veritabanından okur ve dışa aktarmayı sürdürür. Hiçbir fiyat gizleme eklentisi bunu içeriden durduramaz. Akışı kendiniz kapatın.
get_price()fonksiyonunu doğrudan çağıran temalar.get_price_html()üzerinden gitmek yerine ham değeri basan bir tema yukarıdaki bütün filtreleri atlar. Temanızda grep çalıştırın; düzeltmesi tek satırdır, ama önce yerini bulmanız gerekir.- Önbelleğe alınmış sayfalar. Değişikliği yapmadan önce önbelleğe alınmış bir sayfa hâlâ eski fiyatı sunar. Devreye aldıktan sonra önbelleği bir kez temizleyin ve uç önbelleklerin sayfa önbelleğinizden ayrı olduğunu unutmayın.
- Diğer okuma yolları. Fiyatları Store API, REST API, GraphQL ve JSON-LD yapılandırılmış verisi de sunar.
woocommerce_get_price_htmlfiltresini kullanmak bunların döndürdüğü ham sayısal değerlerin hiçbirine dokunmaz. Bütün bunları nasıl düzgün test edeceğinizi görmek isterseniz her WooCommerce eklentisini nasıl sızıntı testinden geçirdiğimizi yazdık; PriceVeil belgeleri eklentinin ele aldığı yüzeyleri anlatıyor ve eklentinin kendi ayarlar ekranı, neyin otomatik olarak korunduğunu ve neyi hâlâ kendinizin kontrol etmesi gerektiğini adlarıyla listeleyen bir kapsam raporu basıyor.
Bir teklif akışı yalnızca bir kod değişikliği değil, aynı zamanda bir süreç değişikliğidir. Birinin talepleri yanıtlaması, üstelik hızlı yanıtlaması gerekir. Teklifte müşterinin satın aldığı şeyin büyük bölümü yanıt süresidir.
Bundan sonra ne yapmalısınız#
Dar başlayın. Bütün kataloğu değiştirmek yerine gerçekten teklif gerektiren ürünleri seçin; kapsamı daraltmak isterseniz belirli ürünler veya kategoriler için fiyat gizleme anlatımı var. Bu yazının başındaki fiyat filtresini ve is_purchasable satırını ekleyin, sepete ekle düğmesinin yerine bir düğme koyun ve onu bugünkü hacminize uyan hangi toplama yöntemiyse ona yönlendirin.
Sonra duyurmadan önce sızıntıları kontrol edin: bir ürün sayfasını gizli pencerede açın, kaynağı görüntüleyip sayıyı arayın ve Store API’nin ne döndürdüğünü görmek için /wp-json/wc/store/v1/products/<id> adresine gidin; hem price_html alanına hem de prices nesnesine bakın. İkisi de temizse çalışan bir WooCommerce teklif isteme akışınız var demektir. Talep hacmi tek bir gelen kutusunun takip edebileceği düzeyi aştığında, talepleri siparişler tablosuna taşıma anı gelmiş demektir; bunu e-postaya dağılmış üç yüz talebiniz olmadan önce yapmak çok daha kolaydır.
