Bir Türk markasının ihracat için İngilizce WooCommerce sitesi vardı. Müşteri “TRY, USD, EUR gösterelim, ödeme USD” dedi. Piyasada WOOCS, CURCY gibi pluginler var ama ya kullanım limitleri vardı, ya fiyat parametreleri statik çeviri yapıyordu, ya da performans bozuyordu. Kod tarafında çözdüm, sonuç temiz oldu. Deneyimi paylaşayım.
İhtiyaç analizi
İhtiyaç şuydu:
– Kullanıcı sitede TRY, USD, EUR seçebilsin.
– Fiyat seçilen kurda görünsün.
– Merkez banka kuruna günlük göre hareket etsin.
– Checkout USD’de yapılsın (iyzico dolar hesabı var).
– Hesaplama matematiksel olarak doğru çalışsın, floating point hatası olmasın.
Plugin pazarında bu kombinasyon için her zaman bir uyumsuzluk buluyordum. Mesela kur otomatik güncelleniyor ama checkout’ta kur dondurulmuyor, kullanıcı farklı kurdan ödüyor. Veya fiyat doğru ama stok sistemine yanlış kur iletiyor.
Tasarım kararları
- Base currency USD. WooCommerce’in native store currency’si USD. Tüm fiyatlar DB’de USD.
- Kur tablosu günlük güncelleniyor. Merkez Bankası API’sinden cron ile.
- Kullanıcının seçtiği currency session cookie’de. URL parametresiyle de set edilebiliyor (
?currency=EUR). - Fiyat gösterilirken USD × kur. Yuvarlama tutar olunca (örneğin 9.99 gibi psikolojik fiyat isteniyor) otomatik en yakın .99’a yuvarlanıyor.
- Checkout her zaman USD. Kullanıcıya şeffaf uyarı, “Ödeme USD olarak gerçekleşecek”.
- Stok yönetimi tek sistemde, kur bağımsız.
Implementasyon parçaları
*Kur tablosu*
wp_options kullanımından kaçındım, wp_ac_exchange_rates diye ayrı tablo açtım:
CREATE TABLE wp_ac_exchange_rates (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
base_currency CHAR(3) NOT NULL,
target_currency CHAR(3) NOT NULL,
rate DECIMAL(18,8) NOT NULL,
fetched_at DATETIME NOT NULL,
UNIQUE KEY uk_pair_date (base_currency, target_currency, fetched_at)
);*Cron ile güncelleme*
WP-Cron yerine gerçek cron kullandım çünkü düşük trafikli sayfalarda WP-Cron güvenilmez. Hostinger cron üzerinden günde üç kez Merkez Bankası XML’inden çekiyor.
*Filter ile fiyat gösterimi*
add_filter('woocommerce_product_get_price', function ($price, $product) {
$currency = ac_get_customer_currency();
if ($currency === 'USD') return $price;
$rate = ac_get_exchange_rate('USD', $currency);
$converted = $price * $rate;
return ac_psychological_round($converted);
}, 10, 2);Benzer filter _sale_price, _regular_price, woocommerce_get_price_html için de eklendi. Tek tek denk getirmek uzadı ama kontrolü elde tuttuğumuz için sonuç temiz.
*Checkout’ta kur dondurma*
Sepet oluşturulurken seçilen currency ve o anki kur order meta’ya yazılıyor:
$order->update_meta_data('_ac_display_currency', $currency);
$order->update_meta_data('_ac_display_rate', $rate);
$order->update_meta_data('_ac_display_total', $displayed_total);Böylece ileride fatura keserken hem USD (gerçek) hem görüntülenen TRY/EUR (müşteri referansı) var.
*Psikolojik yuvarlama*
Basit:
function ac_psychological_round($amount) {
$int = floor($amount);
return $int + 0.99;
}32.40 TRY gösterilmek yerine 32.99 gösteriliyor. Küçük ama satış psikolojisi için belirgin fark.
*Currency switcher*
Header’a üç bayraklı dropdown ekledim. Seçim cookie’ye yazılıyor, sayfa yenileniyor, fiyatlar güncelleniyor.
Cache ile çatışma
LiteSpeed ve page cache, farklı currency için farklı cache üretmeli yoksa kullanıcıya yanlış fiyat görünür. İki yol var: ?currency=TRY gibi URL parametresi cache key olur, veya cookie-based cache variations. Cookie yolunu seçtim, URL daha temiz kaldı.
LiteSpeed’de cache rule’u:
Cache Vary: ac_currencyArtık her currency için ayrı cache saklanıyor.
Karşılaştığım sorunlar
- Discount coupon sabit tutar olursa USD kabul ediyor. “20 TRY indirim” vermek istediğinde ayrı logic ekledim, coupon metadata’sına currency kondu.
- Analytics’e geçen fiyat hangi currency? Google Analytics’te tek currency istendiği için USD’ye normalize ettik.
- Refund hesapları. Refund talebi TRY’ye göre gelirse, order’daki _ac_display_rate ile dönüştürüp USD üzerinden iade.
Plugin vs custom kararı
Başta 70 dolar verip hazır çözümü kullanmak cazip görünüyordu. Ama iki günde tasarım, bir haftada implementasyon tamamlandı. Plugin’de yaşayacağımız ince ayarları başından düşünüp çözdük. Lisans yenileme ücreti yok, plugin breaking change korkusu yok.
Kompleks plugin’lerden önce ihtiyaca bir bakın. Bazen 800 satır kod, 3000 satırlık plugin’den daha hafif ve anlaşılır bir çözüm sunuyor.