Webhook istekleri ağ gecikmesi, zaman aşımı veya sağlayıcının yeniden deneme politikası nedeniyle birden fazla kez ulaşabilir. Bu yüzden yalnızca HTTP 200 döndürmek yeterli değildir. Aynı olayın iki kez işlenmesi çift ödeme, yinelenen e-posta, stok miktarının hatalı azalması veya mükerrer kayıt oluşturulması gibi sonuçlar doğurabilir.
İşleyici tasarlanırken şu sorulara yanıt verilmelidir:
- İstek gerçekten beklenen sağlayıcıdan mı geliyor?
- İmza, ham istek gövdesi üzerinden doğrulanıyor mu?
- Aynı event_id daha önce işlendi mi veya şu anda işleniyor mu?
- İşlem yarıda kalırsa güvenle yeniden başlatılabilir mi?
- Hangi hatalarda sağlayıcı yeniden denemeli?
İmza Doğrulama ve Girdi Güvenliği
İstek gövdesini JSON olarak ayrıştırmadan önce ham gövdeyi alın ve imzayı sunucuda saklanan gizli anahtarla hesaplayın. İmzaları normal eşitlikle karşılaştırmak yerine zamanlama saldırılarına karşı güvenli bir fonksiyon kullanın. Doğrulama başarısızsa ayrıntılı hata vermeden uygun bir 401 veya 403 yanıtı döndürün.
$rawBody = file_get_contents('php://input');
$received = $_SERVER['HTTP_X_SIGNATURE'] ?? '';
$expected = hash_hmac('sha256', $rawBody, $secretKey);
if ($received === '' || !hash_equals($expected, $received)) {
http_response_code(401);
exit('Unauthorized');
}
try {
$data = json_decode($rawBody, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
http_response_code(400);
exit('Invalid payload');
}Üretimde sağlayıcının imza formatını dikkatle uygulayın. Bazı servisler imzaya zaman damgası, nonce veya imza sürümü ekler. Zaman damgası kullanılıyorsa kabul edilen saat farkını sınırlayın; nonce değerlerini de kısa süreli saklayarak aynı imzalı isteğin tekrar oynatılmasını engelleyin. Gizli anahtarları kaynak koduna yazmayın; ortam değişkeni veya güvenli bir sır yönetimi çözümü kullanın.
JSON alanlarını doğrudan SQL sorgularına, HTML çıktısına veya komut satırına taşımayın. Tür, uzunluk, biçim ve izin verilen değerleri doğrulayın. Veritabanı sorgularında parametreli ifadeler, HTML çıktısında ise uygun bağlama göre kaçışlama kullanın.
İdempotensi ve İşlem Akışı
Sağlayıcının benzersiz event_id değerini kalıcı olarak saklayın ve bu alana veritabanı düzeyinde benzersiz kısıt ekleyin. Uygulama seviyesinde “önce var mı?” kontrolü tek başına yeterli değildir; iki eşzamanlı istek aynı sonucu görerek yarış durumuna neden olabilir.
Önerilen akış şöyledir:
- Ham gövde, imza ve temel şema doğrulanır.
- event_id için benzersiz kayıt transaction içinde oluşturulmaya çalışılır.
- Benzersizlik ihlali, olayın daha önce alındığını gösteriyorsa iş etkisi tekrar uygulanmaz.
- İç veritabanı değişiklikleri aynı transaction içinde tamamlanır.
- Uzun süren veya dış servise bağlı işlemler kuyruk ya da outbox modeliyle ayrıştırılır.
Hata Yönetimi, Kuyruklar ve Yeniden Deneme
Geçersiz imza, hatalı JSON, başarısız şema doğrulaması ve yetkisiz olaylar çoğunlukla 4xx sınıfındadır; otomatik yeniden deneme genellikle sorunu çözmez. Geçici veritabanı, ağ veya dış servis sorunları için 5xx yanıtı ve sağlayıcının retry politikasına uygun yeniden deneme tercih edilebilir.
Webhook endpoint’ini gereksiz yere açık tutmayın. İmza doğrulaması ve kalıcı kabul kaydı kısa sürede tamamlandıktan sonra ağır iş kuyruğa bırakılabilir. Kuyruk tüketicisi aynı mesajın iki worker tarafından alınabileceğini varsaymalı; işlem anahtarı, benzersiz kısıt veya dağıtık kilit gibi bir yöntemle idempotensiyi korumalıdır. Başarısız mesajlar için sınırlı deneme, artan bekleme süresi ve inceleme kuyruğu tanımlayın. Rate limit, timeout, alarm ve metrikleri de sağlayıcının davranışına göre belirleyin.
Test Edilebilir Mimari ve Örnek Senaryolar
HTTP katmanını iş kurallarından ayırın. Controller ham isteği alıp doğrulamayı başlatmalı ve uygulama servisini çağırmalıdır. İşleme servisi; veritabanı, kuyruk ve dış servislerle arayüzler üzerinden konuşmalıdır. Böylece birim testlerinde gerçek ödeme sağlayıcısı veya üretim veritabanı kullanmak gerekmez.
En az şu testleri yazın:
- Geçersiz, eksik veya süresi dolmuş imza reddediliyor mu?
- Boş, bozuk veya beklenmeyen alanlara sahip JSON güvenli biçimde ele alınıyor mu?
- Aynı event_id iki kez geldiğinde iş etkisi yalnızca bir kez oluşuyor mu?
- İki eşzamanlı istek yarış durumunda çift kayıt veya çift ödeme oluşturuyor mu?
- Transaction hatasında kısmi durum kalıyor mu?
- Geçici dış servis hatası sınırlı ve artan beklemeli biçimde yeniden deneniyor mu?
- Aynı kuyruk mesajı iki worker tarafından işlendiğinde sonuç değişmeden kalıyor mu?
Yayın Öncesi Kontrol Listesi
- İmza ham gövde üzerinden ve zamanlama güvenli karşılaştırmayla doğrulanıyor.
- event_id için veritabanı düzeyinde benzersizlik sağlanıyor.
- Girdi doğrulama, yetkilendirme ve iş kuralları ayrı katmanlarda uygulanıyor.
- Dış çağrılarda idempotency anahtarı veya uzlaştırma mekanizması bulunuyor.
- Timeout, retry, rate limit, alarm ve gözlemlenebilirlik metrikleri tanımlı.
- Birim, entegrasyon, sözleşme ve eşzamanlılık testleri çalıştırılıyor.