Webhook
Webhook adresinin yazılması, anlık sipariş bildirimleri, imza doğrulama, tekilleştirme ve tekrar deneme.
Her satır API referansındaki tam tanıma gider — alanlar, yanıt kodları ve deneme konsolu orada.
Olaylar
| Alan | Tip | Açıklama |
|---|---|---|
order.created | event | Yeni sipariş. |
order.updated | event | Durum değişimi (restoranda preparing, ready, on_the_way, delivered; Çarşı'da confirmed, preparing, on_the_way, delivered). Restoranda online ödenmiş teslim edilemedi siparişi de bu olayla gelir (deliveryFailed.completedSale: true); iptal değildir. |
order.cancelled | event | Sipariş iptali (müşteri, destek ya da işletme kaynaklı). Çarşı'da teslim edilemedi sipariş de bu olayla gelir. |
order.updated içine gömülmez. Bu olayı işlemezseniz iptal edilen sipariş kasanızda açık kalır. Restoranda online ödenmiş teslim edilemedi sipariş iptal sayılmaz: order.updated ile gelir ve kasada satış olarak kapanır.Gövde: {eventId, event, sentAt, order}. order alanı GET /orders/{id} yanıtıyla aynı nesnedir. Olay tipini gövdedeki event alanından okuyun; başlıklar imzanın kapsamı dışındadır. Kasa yazılımının kendi yazdığı durum webhook olarak geri gönderilmez.
Adresi yazma
Webhook adresini yalnız bu API yazar; işletme panelinde ve mobil uygulamada salt okunur görünür. PUT /webhook adresi, anahtarın bağlı olduğu işletme için kaydeder (iki dikeyde aynı uç); GET /webhook okur, DELETE /webhook kaydı kaldırır (kayıt yoksa da 200 döner) ve teslim durur. Webhook tanımlı değilse sipariş olayları yalnız GET /orders ile izlenir.
curl -X PUT "https://api.supergelsin.com/api/partner/v1/webhook" \
-H "Authorization: Basic $(printf '%s:%s' "$API_KEY" "$API_SECRET" | base64)" \
-H "X-Integrator: Ornek POS" \
-H "X-Operator: kasiyer_01" \
-H "Content-Type: application/json" \
-d '{ "url": "https://ornek-entegrator.example/hook", "events": ["order.created", "order.updated", "order.cancelled"] }'| Alan | Tip | Açıklama |
|---|---|---|
urlzorunlu | string | https:// ile başlayan, kamuya açık adres. Özel ağ adresleri, localhost ve yönlendiren adresler reddedilir (400 invalid_webhook_url). |
secret | string | İmza sırrı. whsec_ öneki ve ardından standart base64 (dolgulu) olmalı; çözülünce 24–64 bayt eder. Gönderilmezse mevcut geçerli sır korunur; yoksa ya da mevcut sır geçerli biçimde değilse yeni bir sır üretilir. |
events | array | Abone olunan olaylar; verilmezse mevcut ayar, yoksa üçü de. |
authHeader · authValue | string | null | Alıcının kendi kimlik başlığı (ör. Authorization); her isteğe eklenir. webhook-id, webhook-timestamp, webhook-signature ve content-type olamaz. |
Üretilen sır yalnız PUT yanıtında açık döner (secret ve secretNote); sonra maskeli görünür (whsec_••••abcd). Kaybolursa yeniden PUT ile yenilenir. Gövde en çok 16 KB olabilir; aşımda 413 payload_too_large döner. GET /webhook ayrıca son teslim denemesinin izini lastDelivery içinde verir (at, status: ok, error, idle).
Sırrı olmayan ya da geçerli biçimde olmayan yapılandırmaya olay gönderilmez; işletmenin panelinde "webhook imza sırrı tanımlı değil" görünür. Her işletmenin kendi anahtarı olduğundan her vitrinin adresi kendi anahtarıyla yazılır; aynı adresi verebilirsiniz.
Tekilleştirme
Teslim en az bir kez yapılır, tam olarak bir kez değil. Aynı olayı birden fazla alabilirsiniz. Her istekteki webhook-id başlığı (gövdedeki eventId ile aynı) bütün denemelerde aynıdır; bunu saklayıp tekrarı eleyin.
<orderId>:order.created
<orderId>:order.cancelled
<orderId>:order.updated:<status>
<orderId>:order.updated:delivery_failed <- restoranda online ödenmiş teslim edilemedisentAt ve webhook-timestamp her denemede değişir. Tekilleştirme için bunları kullanmayın; yalnızca webhook-id kullanın.Sıra garantisi yoktur
Olaylar üretildikleri sırada ulaşmayabilir. Tekrar denenen bir olay, kendisinden sonra üretilmiş bir olaydan sonra ulaşabilir; gecikmiş bir order.updated:preparing, ready olayından sonra gelebilir.
Gövdedeki order.status alanını otorite kabul edin ve kendi kaydınızdan geriye giden bir geçişi yok sayın. Emin olamadığınızda GET /orders/{id} ile uzlaştırın. İstisna: deliveryFailed.completedSale: true ise status: cancelled iptal değil, online ödenmiş satıştır; kasada iptal edilmez.
İmza doğrulama
Tek imza şeması vardır: Standard Webhooks. Başlık webhook-signature: v1,<base64> biçimindedir; imzalanan içerik {webhook-id}.{webhook-timestamp}.{ham gövde}, algoritma HMAC-SHA256'tir. whsec_ önekinden sonraki kısım base64 çözülür ve ham bayt olarak anahtar yapılır; resmî Standard Webhooks kütüphaneleri imzayı bu biçimde doğrular. webhook-timestamp imzaya dahildir: alıcı bunu bir tolerans penceresiyle (öneri: 5 dakika) karşılaştırmalı ve imzayı sabit zamanlı karşılaştırmalıdır.
import crypto from "node:crypto";
function verify(rawBody, headers, secret) {
const id = headers["webhook-id"];
const ts = headers["webhook-timestamp"];
const sig = headers["webhook-signature"]; // "v1,BASE64"
if (!id || !ts || !sig) return false;
// 1) Tekrar oynatma penceresi: 5 dk dışını reddedin.
if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
// 2) İmzalanan içerik: {id}.{timestamp}.{rawBody}; HAM gövdeyi kullanın.
// Anahtar: 'whsec_' sonrası base64 çözülür (ham bayt).
const key = Buffer.from(secret.slice("whsec_".length), "base64");
const expected = "v1," + crypto
.createHmac("sha256", key)
.update(id + "." + ts + "." + rawBody)
.digest("base64");
// 3) Sabit zamanlı karşılaştırma.
const a = Buffer.from(sig), b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}Tekrar deneme ve kalıcı hata
2xx dışındaki her yanıt başarısızlık sayılır. Her deneme 10 saniye bekler. Ne olacağı döndürdüğünüz koda bağlıdır:
| Alan | Tip | Açıklama |
|---|---|---|
5xx · 408 · 429 · timeout · ağ hatası | geçici | Toplam 8 deneme yapılır: anlık deneme, ardından 30 sn, 1, 2, 4, 8, 16 ve 32 dk aralıklarla; toplam yaklaşık 63 dakika. |
diğer 4xx (400, 401, 403, 404, 410, 422 ...) | kalıcı | Aynı içerik aynı reddi üreteceği için yeniden denenmez. |
3xx yönlendirme · güvenli olmayan adres | kalıcı | Yönlendirme yanıtı ve güvenli olmayan adres de kalıcı hatadır. |
Tükenen ya da kalıcı reddedilen olaylar, iki dikeyde de işletme panelindeki teslim sağlığı kartında listelenir. Geçici bir sorun yaşıyorsanız 5xx döndürün; kalıcı bir reddi 4xx ile bildirin. Uzun işleri senkron yapmayın: önce 2xx dönün, işlemeyi kendi kuyruğunuzda sürdürün.
Test ve güvenilirlik modeli
POST /webhook/test ucunuzu canlı sipariş beklemeden doğrular: kayıtlı adrese imzalı bir order.created gönderir. Gövde her zaman uydurma örnek sipariştir (isTest: true); gerçek sipariş ya da müşteri verisi gönderilmez. Olayın webhook-id değeri test-<uuid> biçimindedir; bunu ayırt edip mutfağa ya da toplamaya göndermeyin. Sonuç eşzamanlı döner: alıcı olayı reddetse de yanıt 200 olur ve sonuç gövdededir (ok, httpStatus, permanent, error). Adres tanımlı değilse 409 webhook_not_configured döner.
GET /orders?updatedSince=… ile boşluğu kapatın. Öneri: webhook'u hız için, çekmeyi güvence için birlikte kullanın.