WooCommerce Sipariş Bildirimleri: Webhook ile Gerçek Zamanlı Alma
E-ticaret projelerinde en sık karşılaşılan gereksinimlerden biri, sipariş oluştuğunda bunu anlık olarak dış sistemlere iletmektir. Muhasebe yazılımı, depo yönetim sistemi, Slack kanalı veya özel bir CRM… Hepsinin ortak noktası şu: “Sipariş geldi, ben haberdar olmak istiyorum.” İşte bu noktada WooCommerce’in webhook desteği devreye giriyor. Bu yazıda, WooCommerce webhook’larını sıfırdan nasıl kuracağınızı, gelen veriyi nasıl işleyeceğinizi ve üretim ortamında dikkat etmeniz gereken güvenlik konularını adım adım ele alacağız.
WooCommerce Webhook Nedir?
Webhook, bir olay gerçekleştiğinde WooCommerce’in belirlediğiniz URL’ye otomatik olarak HTTP POST isteği atması anlamına gelir. Klasik API entegrasyonundan farkı şu: Siz sürekli “Yeni sipariş var mı?” diye sormuyorsunuz, WooCommerce size geliyor.
WooCommerce’de webhook tetikleyebilecek olaylar oldukça geniş:
- order.created: Yeni sipariş oluşturulduğunda
- order.updated: Sipariş güncellendiğinde
- order.deleted: Sipariş silindiğinde
- order.restored: Çöp kutusundan geri yüklendiğinde
- product.created: Yeni ürün eklendiğinde
- product.updated: Ürün güncellendiğinde
- customer.created: Yeni müşteri kaydı oluştuğunda
- coupon.created: Kupon oluşturulduğunda
Biz bu yazıda özellikle order.created olayına odaklanacağız çünkü çoğu entegrasyon senaryosunda kritik olan sipariş bildirimidir.
Alıcı Endpoint Hazırlamak
Webhook’u WooCommerce panelinde tanımlamadan önce, veriyi alacak bir endpoint’e ihtiyacınız var. Bu endpoint; HTTP POST isteği kabul eden, JSON body’yi parse edebilen ve 200 HTTP yanıtı döndüren herhangi bir web servisi olabilir.
PHP ile Basit Bir Receiver
En hızlı yol, sitenizin veya ayrı bir sunucunun üzerinde PHP ile basit bir alıcı yazmaktır:
mkdir -p /var/www/html/webhooks
touch /var/www/html/webhooks/woo-order.php
chmod 644 /var/www/html/webhooks/woo-order.php
cat > /var/www/html/webhooks/woo-order.php << 'EOF'
<?php
// WooCommerce Webhook Secret
define('WC_WEBHOOK_SECRET', 'buraya_gizli_anahtarinizi_yazin');
// Log dosyasi
define('LOG_FILE', '/var/log/woo-webhooks/orders.log');
// Imzayi dogrula
function verify_webhook_signature($payload, $signature) {
$computed = base64_encode(hash_hmac('sha256', $payload, WC_WEBHOOK_SECRET, true));
return hash_equals($computed, $signature);
}
// Log yaz
function write_log($message) {
$timestamp = date('Y-m-d H:i:s');
file_put_contents(LOG_FILE, "[{$timestamp}] {$message}" . PHP_EOL, FILE_APPEND);
}
// Giris
$raw_payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_WC_WEBHOOK_SIGNATURE'] ?? '';
if (empty($signature)) {
http_response_code(401);
write_log("HATA: Imza eksik");
exit;
}
if (!verify_webhook_signature($raw_payload, $signature)) {
http_response_code(401);
write_log("HATA: Gecersiz imza - IP: " . $_SERVER['REMOTE_ADDR']);
exit;
}
$order = json_decode($raw_payload, true);
if (json_last_error() !== JSON_ERROR_NONE) {
http_response_code(400);
write_log("HATA: JSON parse hatasi");
exit;
}
// Siparis bilgilerini isle
$order_id = $order['id'] ?? 'bilinmiyor';
$order_total = $order['total'] ?? '0';
$customer_email = $order['billing']['email'] ?? 'yok';
$order_status = $order['status'] ?? 'bilinmiyor';
write_log("YENI SIPARIS: #{$order_id} | Toplam: {$order_total} TL | Musteri: {$customer_email} | Durum: {$order_status}");
// Buraya kendi is mantiginizi ekleyin
// process_order($order);
http_response_code(200);
echo json_encode(['success' => true, 'order_id' => $order_id]);
EOF
Log dizinini de oluşturalım:
mkdir -p /var/log/woo-webhooks
chown www-data:www-data /var/log/woo-webhooks
chmod 755 /var/log/woo-webhooks
Python Flask ile Receiver
Eğer ayrı bir mikroservis yapısı kullanıyorsanız Python Flask daha temiz bir çözüm sunar:
pip install flask requests
cat > /opt/woo-webhook-receiver/app.py << 'EOF'
import hmac
import hashlib
import base64
import json
import logging
from flask import Flask, request, jsonify
from datetime import datetime
app = Flask(__name__)
WEBHOOK_SECRET = "buraya_gizli_anahtarinizi_yazin"
logging.basicConfig(
filename='/var/log/woo-webhooks/flask-orders.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
def verify_signature(payload_body: bytes, signature: str) -> bool:
computed = base64.b64encode(
hmac.new(
WEBHOOK_SECRET.encode('utf-8'),
payload_body,
hashlib.sha256
).digest()
).decode('utf-8')
return hmac.compare_digest(computed, signature)
@app.route('/webhook/woo-order', methods=['POST'])
def handle_order():
signature = request.headers.get('X-WC-Webhook-Signature', '')
if not signature:
logging.warning(f"Imzasiz istek - IP: {request.remote_addr}")
return jsonify({'error': 'Imza eksik'}), 401
if not verify_signature(request.data, signature):
logging.warning(f"Gecersiz imza - IP: {request.remote_addr}")
return jsonify({'error': 'Gecersiz imza'}), 401
order = request.get_json()
order_id = order.get('id', 'bilinmiyor')
total = order.get('total', '0')
email = order.get('billing', {}).get('email', 'yok')
status = order.get('status', 'bilinmiyor')
logging.info(f"Yeni siparis: #{order_id} | Toplam: {total} | Musteri: {email} | Durum: {status}")
# Slack bildirimi, veritabani kaydi vs. buraya
return jsonify({'success': True, 'order_id': order_id}), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
EOF
WooCommerce Panelinde Webhook Tanımlama
Endpoint hazır olduğuna göre WooCommerce tarafında webhook’u oluşturabiliriz.
WooCommerce > Ayarlar > Gelişmiş > Webhooks yolunu izleyin. “Webhook Ekle” butonuna tıklayın.
Doldurmanız gereken alanlar:
- Ad: “Yeni Sipariş Bildirimi” gibi tanımlayıcı bir isim verin
- Durum: Aktif olarak ayarlayın
- Konu: “Sipariş Oluşturuldu” seçin
- Teslim URL’si: Endpoint adresinizi girin (https://receiver.example.com/webhook/woo-order)
- Gizli: Güçlü bir rastgele string girin, bu imzalama için kullanılacak
- API Sürümü: WP REST API v3 seçin
Gizli anahtar oluşturmak için:
openssl rand -base64 32
# Ornek cikti: K8mP2xQvR9nL4wE7jT1yA5oF3uH6cB0dN
Bu değeri hem WooCommerce paneline hem de endpoint kodunuza yazın.
Webhook Güvenliği: İmzalama Mekanizması
WooCommerce, her webhook isteğinde X-WC-Webhook-Signature başlığını gönderir. Bu başlık, payload’ın HMAC-SHA256 ile imzalanmış ve base64 ile encode edilmiş halidir.
İmzalama mantığını anlamak için:
# Komut satiriyla test edebilirsiniz
SECRET="K8mP2xQvR9nL4wE7jT1yA5oF3uH6cB0dN"
PAYLOAD='{"id":123,"status":"processing"}'
echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -binary | base64
# Bu ciktiyi WooCommerce'in gonderdigi imzayla karsilastirabilirsiniz
Neden imzayı doğrulamak zorunlu? Çünkü endpoint URL’niz dışarıdan erişilebilir durumdadır. İmza doğrulaması olmadan herkes sahte sipariş verisi gönderebilir ve sisteminizi manipüle edebilir.
Nginx ile SSL Terminasyonu
Production ortamında webhook endpoint’iniz mutlaka HTTPS üzerinden çalışmalı. WooCommerce zaten HTTP endpoint’lere güvenmez. Nginx konfigürasyonu:
cat > /etc/nginx/sites-available/woo-webhook << 'EOF'
server {
listen 443 ssl http2;
server_name receiver.example.com;
ssl_certificate /etc/letsencrypt/live/receiver.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/receiver.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Webhook icin maksimum body boyutu
client_max_body_size 1m;
location /webhook/ {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WooCommerce imza basligini ilet
proxy_pass_header X-WC-Webhook-Signature;
# Timeout ayarlari
proxy_read_timeout 30s;
proxy_connect_timeout 10s;
}
}
EOF
ln -s /etc/nginx/sites-available/woo-webhook /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
Webhook Test Etme
WooCommerce panelinden “Ping Gonder” butonu ile test edebilirsiniz. Ama daha kontrollü bir test için curl kullanabilirsiniz:
SECRET="K8mP2xQvR9nL4wE7jT1yA5oF3uH6cB0dN"
# Test payload
PAYLOAD='{"id":9999,"status":"processing","total":"149.90","currency":"TRY","billing":{"first_name":"Test","last_name":"Kullanici","email":"[email protected]","phone":"05001234567"},"line_items":[{"id":1,"name":"Test Urun","quantity":2,"total":"149.90"}]}'
# HMAC hesapla
SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" -binary | base64)
echo "Imza: $SIGNATURE"
# Webhook gonder
curl -v -X POST
-H "Content-Type: application/json"
-H "X-WC-Webhook-Source: https://maazaniz.com"
-H "X-WC-Webhook-Topic: order.created"
-H "X-WC-Webhook-Resource: order"
-H "X-WC-Webhook-Event: created"
-H "X-WC-Webhook-Signature: $SIGNATURE"
-d "$PAYLOAD"
https://receiver.example.com/webhook/woo-order
Gerçek Dünya Senaryosu: Slack Bildirimi
Şimdi işe yarayan bir senaryo kuralım. Yeni sipariş geldiğinde Slack kanalına bildirim gönderelim:
cat > /opt/woo-webhook-receiver/slack_notifier.py << 'EOF'
import requests
import json
SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/T.../B.../..."
def format_order_message(order: dict) -> dict:
order_id = order.get('id', '?')
total = order.get('total', '0')
currency = order.get('currency', 'TRY')
billing = order.get('billing', {})
ad_soyad = f"{billing.get('first_name', '')} {billing.get('last_name', '')}".strip()
email = billing.get('email', 'yok')
telefon = billing.get('phone', 'yok')
items = order.get('line_items', [])
urun_listesi = "n".join([
f"- {item.get('name', '?')} x{item.get('quantity', 1)} ({item.get('total', '0')} {currency})"
for item in items
])
return {
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": f"Yeni Siparis #{order_id}"
}
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": f"*Musteri:*n{ad_soyad}"},
{"type": "mrkdwn", "text": f"*E-posta:*n{email}"},
{"type": "mrkdwn", "text": f"*Telefon:*n{telefon}"},
{"type": "mrkdwn", "text": f"*Toplam:*n{total} {currency}"}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": f"*Urunler:*n{urun_listesi}"
}
}
]
}
def send_slack_notification(order: dict) -> bool:
try:
message = format_order_message(order)
response = requests.post(
SLACK_WEBHOOK_URL,
json=message,
timeout=10
)
return response.status_code == 200
except Exception as e:
print(f"Slack bildirimi gonderilirken hata: {e}")
return False
EOF
Webhook Başarısız Olduğunda: Retry Mekanizması
WooCommerce, başarısız webhook teslimatlarını otomatik olarak yeniden dener. Varsayılan olarak 5 denemeden sonra webhook’u devre dışı bırakır. Bu davranışı wp-config.php ile değiştirebilirsiniz:
# wp-config.php dosyasina ekleyin
cat >> /var/www/html/wp-config.php << 'EOF'
// Webhook retry sayisi (varsayilan: 5)
define('WC_WEBHOOK_DELIVERY_TIMEOUT', 10);
EOF
Kendi tarafınızda da bir kuyruk mekanizması kurabilirsiniz. Basit bir Redis tabanlı yaklaşım:
cat > /opt/woo-webhook-receiver/queue_handler.py << 'EOF'
import redis
import json
import time
import threading
import logging
r = redis.Redis(host='localhost', port=6379, db=0)
QUEUE_KEY = "woo:order:queue"
FAILED_KEY = "woo:order:failed"
def enqueue_order(order: dict):
"""Siparisi kuyruga ekle"""
r.lpush(QUEUE_KEY, json.dumps(order))
logging.info(f"Siparis kuyruga eklendi: #{order.get('id')}")
def process_queue():
"""Kuyrugu isle - ayri bir thread'de calistir"""
while True:
try:
item = r.brpop(QUEUE_KEY, timeout=5)
if item:
_, order_data = item
order = json.loads(order_data)
order_id = order.get('id', '?')
try:
# Asil islemi yap
# process_order(order)
logging.info(f"Siparis islendi: #{order_id}")
except Exception as e:
logging.error(f"Siparis isleme hatasi #{order_id}: {e}")
# Basarisiz siparisi ayri bir listeye ekle
r.lpush(FAILED_KEY, order_data)
except Exception as e:
logging.error(f"Kuyruk hatasi: {e}")
time.sleep(1)
# Worker thread baslat
worker = threading.Thread(target=process_queue, daemon=True)
worker.start()
EOF
Webhook Loglarını İzleme
Üretim ortamında webhook loglarını düzenli izlemek şarttır. Basit bir izleme scripti:
cat > /usr/local/bin/woo-webhook-monitor.sh << 'EOF'
#!/bin/bash
LOG_FILE="/var/log/woo-webhooks/orders.log"
ALERT_EMAIL="[email protected]"
# Son 5 dakikadaki hatalari say
SON_5_DK=$(date -d '5 minutes ago' '+%Y-%m-%d %H:%M:%S')
HATA_SAYISI=$(awk -v tarih="$SON_5_DK" '$0 >= tarih && /HATA/' "$LOG_FILE" | wc -l)
if [ "$HATA_SAYISI" -gt 10 ]; then
echo "Son 5 dakikada $HATA_SAYISI webhook hatasi tespit edildi!" |
mail -s "UYARI: Webhook Hata Spike" "$ALERT_EMAIL"
fi
# Gunluk rapor
BUGUN=$(date '+%Y-%m-%d')
TOPLAM=$(grep "$BUGUN" "$LOG_FILE" | grep "YENI SIPARIS" | wc -l)
echo "[$BUGUN] Toplam islenen siparis: $TOPLAM"
EOF
chmod +x /usr/local/bin/woo-webhook-monitor.sh
# Cron'a ekle
echo "*/5 * * * * root /usr/local/bin/woo-webhook-monitor.sh >> /var/log/woo-webhook-monitor.log 2>&1"
>> /etc/cron.d/woo-webhook
Sık Karşılaşılan Sorunlar
408 Timeout Hatası: WooCommerce varsayılan olarak 10 saniyede yanıt bekler. Endpoint’iniz ağır işlemler yapıyorsa, isteği hemen kabul edip asenkron işleyin.
İmza Doğrulama Başarısız: Payload’ı işlemeden önce raw body olarak okuyun. JSON parse ettikten sonra imzalamaya çalışırsanız boşluk farkları nedeniyle eşleşmez.
Duplicate Webhook: Bazen WooCommerce aynı olayı birden fazla gönderebilir. X-WC-Webhook-Delivery-ID başlığını kullanarak idempotency sağlayın:
# Ornek: delivery_id kontrolu
DELIVERY_ID=$(echo "$HTTP_X_WC_WEBHOOK_DELIVERY_ID")
# Bu ID'yi Redis/DB'de kontrol et, daha once islenmisse atla
SSL Sertifika Sorunu: Receiver sunucunuzun SSL sertifikası geçerli olmalı. Self-signed sertifikalar WooCommerce tarafından reddedilir.
WooCommerce REST API ile Webhook Oluşturma
Webhook’ları panelden değil API üzerinden de yönetebilirsiniz. Bu özellikle CI/CD pipeline’larında işe yarar:
# WooCommerce API anahtari olusturun (WooCommerce > Ayarlar > Gelismis > REST API)
CK="ck_xxxxxxxxxxxx" # Consumer Key
CS="cs_xxxxxxxxxxxx" # Consumer Secret
SITE="https://maazaniz.com"
# Webhook olustur
curl -X POST "$SITE/wp-json/wc/v3/webhooks"
-u "$CK:$CS"
-H "Content-Type: application/json"
-d '{
"name": "Siparis Bildirimi",
"topic": "order.created",
"delivery_url": "https://receiver.example.com/webhook/woo-order",
"secret": "K8mP2xQvR9nL4wE7jT1yA5oF3uH6cB0dN",
"status": "active"
}'
# Mevcut webhook listesini goruntule
curl -X GET "$SITE/wp-json/wc/v3/webhooks"
-u "$CK:$CS" | python3 -m json.tool
Sonuç
WooCommerce webhook’ları, doğru kurulduğunda gerçekten güçlü bir entegrasyon altyapısı sağlar. Bu yazıda ele aldığımız noktaları özetlemek gerekirse:
- Güvenlik önce gelir: İmza doğrulamasını asla atlamayın. Endpoint’iniz herkese açık olduğundan sahte istek riski gerçektir.
- Hızlı yanıt, asenkron işlem: Endpoint’iniz 200 döndürmeli, ağır işlemleri arka plana almalıdır. Timeout sorunu yaşamamak için bu kritiktir.
- Log her şeyi: Özellikle production ortamında ne zaman, hangi sipariş için ne yapıldığını kayıt altına alın. Hata ayıklarken bu loglar hayat kurtarır.
- Idempotency unutulmasın: Aynı webhook iki kez gelebilir. Sisteminiz bunu tolere edebilecek şekilde tasarlanmalıdır.
- HTTPS zorunlu: Hem güvenlik hem de WooCommerce uyumluluğu açısından HTTP endpoint kullanmayın.
Gerçek dünyada bu yapıyı kurarken en çok zaman kaybedilen nokta genellikle imza doğrulamasıdır, özellikle raw body yerine parse edilmiş body kullanıldığında. Bu detaya dikkat ederseniz geri kalan kısım oldukça düzgün çalışır. Sorularınız için yorum bırakabilirsiniz.
