Kurye Webhook v1
Haldeki kurye webhook API'si — URL yapısı, HMAC imza doğrulama, aksiyon sözlüğü, yanıt kodları ve idempotency kuralları.
Haldeki'nin kurye partnerlerinden sipariş durum bildirimi almak için sunduğu tek sözleşme — v1. Bu doküman yalnız dışa verilen v1 API'yi anlatır.
Genel Bakış
Bir kurye partneri sipariş durumu değiştikçe (onaylandı, yolda, teslim edildi, iptal) Haldeki'ye tek bir URL'e POST atar. Model at-least-once'tur: partner 2xx dışı her yanıtta üstel geri çekilmeyle tekrar denemelidir. Haldeki tarafı idempotent'tir — aynı olay ikinci kez gelirse yan etki üretmez, yine 200 döner.
Her kurye hesabına, kayıt sırasında üretilen tek bir URL verilir. Bu URL provider'a ve hesaba özeldir; başka bir hesap veya provider için kullanılamaz.
Bu sözleşme Haldeki'nindir ve sağlayıcıdan bağımsızdır: hangi kurye firması entegre olursa olsun aynı v1 sözleşmesini, aynı aksiyonları ve aynı imza şemasını kullanır. Sözleşme belirli bir partnere özel değildir.
URL Yapısı
https://haldeki.com/api/webhooks/v1/courier/{provider}/{slug}[/{action}]
| Segment | Açıklama |
|---|---|
v1 | Sözleşme versiyonu, path'te. Kırıcı değişiklikte yeni bir v2 path açılır; v1 en az 6 ay çalışır kalır. |
provider | Hesap açılışında Haldeki'nin partnere ilettiği sağlayıcı anahtarı (ör. {provider}). |
slug | Hesaba özel, rastgele üretilmiş gizli kimlik. URL güvenlik sınırı değildir, asıl doğrulama imzadadır (aşağıda) — ama slug yine de paylaşılmaz, üçüncü tarafa sızdırılmaz. |
action | Opsiyonel — bkz. Gelen İstek. Yalnızca v1 dışa verilir; versiyonsuz eski path iç kullanım içindir. |
Kimlik Doğrulama / İmza
Her POST gövdesi ortak gizli anahtarla (secret) imzalanır ve x-provider-signature header'ında
taşınır:
x-provider-signature: hex( HMAC-SHA256( raw_body, secret ) )
Secret çift yönlüdür: partnerin bize gönderdiği statü bildirimi de, bizim partnere gönderdiğimiz sipariş de aynı anahtarla imzalanır. Karşılaştırma sabit zamanlıdır (timing saldırısına kapalı).
Önerilen — güçlendirilmiş mod
İstek x-webhook-timestamp header'ı (unix saniye) taşıyorsa imza şu şekilde hesaplanır ve
±5 dakika tazelik penceresi dışındaki istekler reddedilir (replay koruması):
x-provider-signature: hex( HMAC-SHA256( timestamp + "." + raw_body, secret ) )Bu mod canlıdır ve önerilendir. x-webhook-timestamp göndermeyen istekler eski şemayla
doğrulanmaya devam eder (ileride zorunlu kılınabilir).
Gelen İstek
Birincil biçim aksiyon uç noktasıdır — gövde yalnız sipariş kimliğini taşır, statü anlamı URL'deki aksiyondan gelir:
curl -X POST \
https://haldeki.com/api/webhooks/v1/courier/{provider}/{slug}/delivered \
-H "Content-Type: application/json" \
-H "x-provider-signature: $(echo -n '{"orderId":"hld_1234"}' | \
openssl dgst -sha256 -hmac '<SECRET>' | sed 's/^.* //')" \
-d '{"orderId":"hld_1234"}'| Alan | Tip | Zorunlu | Açıklama |
|---|---|---|---|
orderId | string | Evet (pid ile ikisinden biri) | Sipariş kimliği |
pid | string | Evet (orderId ile ikisinden biri) | Sipariş kimliği |
event | string | Hayır | Opsiyonel |
timestamp | ISO veya epoch | Hayır | Verilmezse işlenme anı esas alınır |
Aksiyonsuz tek-URL modu (gövdede status alanı) geriye dönük uyumluluk için desteklenir; yeni
entegrasyonlar aksiyon uç noktalarını kullanmalıdır. Gövde JSON, en fazla 128KB. Bilinmeyen
alanlar yok sayılır.
Aksiyon Sözlüğü
Haldeki'nin kendi kanonik aksiyonları — sayısal bir statü sözlüğü dayatılmaz, her entegratör kendi iç durum kodlarını bu aksiyonlara kendi tarafında eşler:
| Aksiyon | Haldeki tarafında |
|---|---|
approve | Sipariş "hazırlanıyor"a geçer, kurye atandı |
on-the-way | Sipariş "yolda"ya geçer, kurye yolda |
delivered | Sipariş "teslim edildi"ye geçer |
cancel | Otomatik kapanmaz — para/stok sonucu olduğu için operasyon mutabakat kuyruğuna düşer, insan inceleyip kapatır |
İptal Nedenleri
İptal bildirimi öncesi geçerli neden listesi imzasız çekilebilir (sabit liste, sır içermez):
curl https://haldeki.com/api/webhooks/v1/courier/{provider}/{slug}/cancel-reasonscancel gövdesi reasonId (sayı) veya reason (kod) alanlarından birini, opsiyonel note
alanını taşır. Bilinmeyen neden 400 unknown_reason döner ve yanıtta geçerli liste yer alır.
| id | code | name |
|---|---|---|
| 1 | customer_unreachable | Müşteriye ulaşılamadı |
| 2 | address_not_found | Adres bulunamadı / hatalı |
| 3 | customer_rejected | Müşteri teslimatı kabul etmedi |
| 4 | out_of_stock | Ürün temin edilemedi |
| 5 | courier_unavailable | Kurye bulunamadı |
| 6 | store_closed | İşletme kapalı |
| 7 | weather_or_traffic | Hava/trafik nedeniyle teslim edilemedi |
| 99 | other | Diğer |
Numaralar kalıcıdır: yeni neden eklendiğinde sona eklenir, mevcut bir numara başka bir nedene devredilmez.
Giden İstek
Haldeki bir siparişi partnere gönderirken aynı header ile imzalar ve tekrar denemelerde aynı kalan
bir X-Idempotency-Key ekler. İşletme eşlemesi restaurantId alanıyla yapılır — bu değerleri
Haldeki üretir ve iletir (düz sayı, ör. 1001); partnerin kendi ID sistemine bağlı değildir.
Kayıt Doğrulama (validate)
Kayıt sırasında elindeki secret ve işletme kimliğinin doğru olduğunu teyit etmek için partner
validate aksiyonuna aynı imza şemasıyla istek atabilir. Sipariş akışına hiç girmez, yalnızca
kimliği doğrular.
{"restaurantId":"1001"}Yanıt Kodları
| Kod | Anlamı | Ne yapmalı |
|---|---|---|
200 | İşlendi (duplicate dahil) | Bitti |
400 | Gövde bozuk | Düzelt, tekrar dene |
401 | İmza yanlış / hesap pasif | Secret ve URL'i kontrol et |
404 | Sipariş henüz bizde yok (order_not_found) | Bekle, tekrar dene |
413 | Gövde > 128KB | Gövdeyi küçült |
429 | Hız sınırı aşıldı | Retry-After kadar bekle, tekrar dene |
500 | Beklenmeyen hata | Tekrar dene |
Tekrar Deneme ve Idempotency
Aynı olay iki kez gelirse Haldeki yan etki üretmez, yine 200 döner (outcome: duplicate).
Partnerin tekrar denemesi asla zarar vermez. İstek timestamp içermiyorsa işlenme anı (now())
esas alınır.
Sürümleme
Bu doküman v1 sözleşmesini anlatır. Alan eklemek (yeni JSON field, yeni action) kırıcı
değişiklik sayılmaz, versiyon artırmaz. Kırıcı bir değişiklik gerekirse yeni bir v2 path açılır;
v1 en az 6 ay çalışır durumda kalır.