Haldeki Geliştirici
Kurye

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}]
SegmentAçıklama
v1Sö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.
providerHesap açılışında Haldeki'nin partnere ilettiği sağlayıcı anahtarı (ör. {provider}).
slugHesaba ö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.
actionOpsiyonel — 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:

POST/api/webhooks/v1/courier/{provider}/{slug}/{action}
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"}'
AlanTipZorunluAçıklama
orderIdstringEvet (pid ile ikisinden biri)Sipariş kimliği
pidstringEvet (orderId ile ikisinden biri)Sipariş kimliği
eventstringHayırOpsiyonel
timestampISO veya epochHayırVerilmezse 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:

AksiyonHaldeki tarafında
approveSipariş "hazırlanıyor"a geçer, kurye atandı
on-the-waySipariş "yolda"ya geçer, kurye yolda
deliveredSipariş "teslim edildi"ye geçer
cancelOtomatik 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):

GET/api/webhooks/v1/courier/{provider}/{slug}/cancel-reasons
curl https://haldeki.com/api/webhooks/v1/courier/{provider}/{slug}/cancel-reasons

cancel 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.

idcodename
1customer_unreachableMüşteriye ulaşılamadı
2address_not_foundAdres bulunamadı / hatalı
3customer_rejectedMüşteri teslimatı kabul etmedi
4out_of_stockÜrün temin edilemedi
5courier_unavailableKurye bulunamadı
6store_closedİşletme kapalı
7weather_or_trafficHava/trafik nedeniyle teslim edilemedi
99otherDiğ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.

POST/api/webhooks/v1/courier/{provider}/{slug}/validate
{"restaurantId":"1001"}

Yanıt Kodları

KodAnlamıNe yapmalı
200İşlendi (duplicate dahil)Bitti
400Gövde bozukDüzelt, tekrar dene
401İmza yanlış / hesap pasifSecret ve URL'i kontrol et
404Sipariş henüz bizde yok (order_not_found)Bekle, tekrar dene
413Gövde > 128KBGövdeyi küçült
429Hız sınırı aşıldıRetry-After kadar bekle, tekrar dene
500Beklenmeyen hataTekrar 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.