Teslimat Platformları
Operatör menü yolu: Ayarlar → Online Sipariş • URL: /admin/settings/online-orders
Erişim: ADMIN (yapılandırma), MANAGER (görüntüleme/test/dükkân aç-kapat) • Plan: deliveryIntegration özelliği olan planlar
Bu entegrasyon restoranınızı Yemeksepeti, Getir, Trendyol Yemek ve Migros Yemek platformlarına bağlar. Gelen siparişler doğrudan bir HummyTummy Order kaydına dönüşür, mutfak ekranına (KDS) düşer ve mutfak yazıcısına otomatik basılır. Tüm akış tek bir module (delivery-platforms) içinde toplanmıştır; siparişler, menü senkronu ve dükkân aç/kapat aynı adaptör katmanı üzerinden çalışır.
Bu sayfa geliştirici referansıdır (uç noktalar, imzalar, yaşam döngüsü). Operatör kurulumu için yardım portalındaki Online Sipariş sayfasına bakın.
1. Platform başına bağlantı
Her platform için tenant başına tek bir yapılandırma (DeliveryPlatformConfig) tutulur. Operatör, kimlik bilgilerini Ayarlar → Online Sipariş ekranından girer; sunucuda şifrelenerek saklanır (yanıtlarda asla geri dönmez, yalnızca hasCredentials / hasAccessToken bayrakları döner).
Yapılandırma alanları
| Alan | Açıklama |
|---|---|
platform | YEMEKSEPETI · GETIR · TRENDYOL · MIGROS |
remoteRestaurantId | Platformun verdiği mağaza/şube/zincir kimliği. Gelen webhook ve poll çağrıları bu kimlik üzerinden tenant’a eşlenir. |
credentials | Platforma özel anahtarlar (aşağıda). |
branchId | Bu platformun siparişlerini alacak şube. Çok şubeli routing: platform başına bir yapılandırma → bir şube. Boş/null ise “ilk aktif şube” yedeği uygulanır. |
environment | production (canlı) veya sandbox (test uç noktaları + test siparişi simülatörü). Varsayılan production. |
autoAccept | true ise gelen sipariş otomatik kabul edilir; false ise PENDING_APPROVAL olarak bekler. |
Platforma göre kimlik bilgileri
Her adaptör farklı bir kimlik doğrulama yöntemi kullanır:
OAuth client-credentials. POST /v2/login ile clientId + clientSecret gönderilir, dönen access_token saklanır (süre dolmadan 5 dk önce yenilenir). Menü ve dükkân-durum çağrıları için ek olarak chainCode ve posVendorId kullanılır.
credentials = { clientId, clientSecret, chainCode?, posVendorId? }remoteRestaurantId, platform + restoran kimliği çiftinde benzersizdir
(@@unique([platform, remoteRestaurantId])). Aynı mağaza kimliğini ikinci bir
tenant/şube için kaydetmeye çalışmak çakışma hatası döndürür.
2. Sandbox / test modu
Canlı bir platform hesabı hiç bağlanmadan tüm ingest → KDS → yazıcı hattını doğrulayabilirsiniz. Yapılandırmanın environment alanını sandbox yapın ve test siparişi tetikleyin.
Yapılandırmayı sandbox’a alın
PATCH /delivery-platforms/configs/:platform ile { "environment": "sandbox" } gönderin. Sandbox modunda adaptör, platformun test uç noktalarına yönlenir (Trendyol için stageapi.trendyol.com; diğerleri için sandbox host’u henüz yayınlanmadığından üretim host’una düşer, ilgili *_SANDBOX_API_BASE_URL ortam değişkeniyle ezilebilir).
Test siparişi gönderin
POST /delivery-platforms/test-order/:platform (ADMIN)Bu uç nokta, gerçek webhook’ların kullandığı aynı processIncomingOrder hattından sentetik bir sipariş geçirir. Yanıt:
{ "simulated": true, "orderId": "...", "orderNumber": "YEM-...", "externalOrderId": "TEST-<uuid>", "status": "PENDING_APPROVAL" }Sonucu doğrulayın
Sipariş; KDS’te [TEST] etiketli kalemlerle görünür, mutfak yazıcısına basılır ve externalOrderId sabit TEST- ön ekiyle gelir — gerçek bir siparişle karıştırılamaz. Eşlenmiş ürününüz varsa simülatör bunları kullanır; yoksa açıkça [TEST] etiketli, eşlenmemiş kalemlerle (notlara yazılır) PENDING_APPROVAL üretir.
Test siparişi yalnızca sandbox yapılandırmalarda çalışır. production
bir yapılandırmada uç nokta reddeder — böylece sentetik bir sipariş asla canlı
platforma kabul/iletme yapamaz.
3. Gelen sipariş yaşam döngüsü
Taşıma: webhook vs. yoklama (polling)
| Platform | Yöntem | Cadence |
|---|---|---|
| Yemeksepeti | Webhook (gerçek zamanlı push) | — |
| Trendyol | Webhook + yoklama | ~15 sn |
| Getir | Yoklama | ~15 sn |
| Migros | Yoklama | ~20 sn |
İki taşıma da aynı yere iner: adaptör ham gövdeyi NormalizedOrder biçimine çevirir, processIncomingOrder bunu bir Order’a dönüştürür. Dedup garantisi orders(tenantId, source, externalOrderId) üzerindeki kısmi benzersiz index’tir (eşzamanlı çift webhook P2002 ile sessizce yutulur).
Yönlendirme, otomatik kabul ve onay kapısı
- Şube routing:
config.branchIdbu tenant’ın aktif bir şubesini gösteriyorsa sipariş oraya; aksi halde en eski aktif şubeye düşer. - Otomatik kabul:
autoAcceptaçıksa siparişPENDING(mutfak kuyruğunda) oluşturulur ve platform tarafında da kabul edilir. - Onay zorlama:
autoAcceptkapalıysa veya eşlenmemiş kalem varsa veya platform toplamları kalem toplamından %5’ten (veya 1₺‘den) fazla saparsa siparişPENDING_APPROVALolur — bir operatör onaylayana dek mutfağa girmez.
Operatör aksiyonları
POST /delivery-platforms/orders/:orderId/accept { prepTimeMinutes? }
POST /delivery-platforms/orders/:orderId/reject { reason } (zorunlu)
POST /delivery-platforms/orders/:orderId/prep-time { minutes } (1–240)- Kabul:
PENDING_APPROVAL → PENDING; platformaacceptOrdergönderilir. İsteğe bağlı hazırlık süresiyle birlikte yapılabilir. Zaten kabul edilmiş sipariş için no-op (alreadyAccepted: true). - Ret:
reasonzorunludur ve platforma iletilir (müşteri/kurye nedeni görür). YalnızcaPENDING_APPROVAL/PENDINGreddedilebilir; sonrası iptal akışıdır. - Hazırlık süresi: platforma
markPreparinggönderir ve siparişiPREPARING’e ilerletir.
Dürüstlük sözleşmesi: adaptör çağrısı başarısız olursa hata yukarı taşınır, iç durum ilerletilmez ve hata kaydedilir (log + devre kesici).
İptal, değişiklik ve iade (gelen)
Bu olaylar yalnızca webhook’lu iki platformda (Yemeksepeti, Trendyol) uç nokta olarak vardır; Getir + Migros’ta yoklama üzerinden yüzeye çıkar.
- Durum güncellemesi / iptal —
PICKED_UP/DELIVERED→SERVED;CANCELLED/REJECTED→CANCELLED. Atomik ve idempotenttir; terminal bir siparişi geri sektirmez. Yalnızca iç durumu değiştirir, platforma geri bildirim göndermez. - Değişiklik (amendment) — platform, mutfak işlemeden önce sipariş kalemlerini değiştirir. Tam sepet yeniden çözümlenir, toplamlar aynı drift-güvenli mantıkla yeniden hesaplanır, KDS’e yeniden yayınlanır. Sipariş
READY/SERVED/PAID/CANCELLEDise reddedilir (kâğıt fiş ile pişen yemeğin senkronu bozulmasın diye); platform iptal + yeniden sipariş etmelidir. - İade (refund) — platform iadeyi başlatır (parayı platform tutar). Biz yalnızca yansıtırız, geri bildirim göndermeyiz:
- Tam iade → sipariş
CANCELLEDolur (cancelledAtişlenir). - Kısmi iade → durum korunur.
- Tam iade → sipariş
Kısmi iade tutarı dürüst sınır: Order modelinde özel bir iade tutarı
kolonu yoktur ve teslimat siparişleri Payment satırı oluşturmaz. Bu yüzden
kısmi tutar yalnızca siparişin externalData.refunds[] defterinde,
notlarında, teslimat logunda ve yayınlanan
delivery.order.refunded.v1 olayında durur. Muhasebe/raporlama bu kaynakları
okumalıdır.
Restoran-başlatımlı iade desteklenmez. Dört platformun hiçbiri restoran
POS’undan iade uç noktası belgelemez (refundOrder opsiyoneldir ve hiçbir
adaptör uygulamaz). İade platformun kendi panelinden yapılır; biz onu yukarıdaki
gelen iade webhook’uyla yansıtırız.
4. Webhook URL’leri ve imzalar
Webhook uç noktaları Public ama WebhookAuthGuard ile korunur ve dakikada 60 istekle hız sınırlıdır. :remoteId her zaman remoteRestaurantId’dir.
POST /webhooks/delivery/yemeksepeti/order/:remoteId
PUT /webhooks/delivery/yemeksepeti/:remoteId/order/:remoteOrderId/status
POST /webhooks/delivery/yemeksepeti/:remoteId/order/:remoteOrderId/refund
PUT /webhooks/delivery/yemeksepeti/:remoteId/order/:remoteOrderId/amendİmza: Authorization: Bearer <JWT>. JWT, YEMEKSEPETI_WEBHOOK_SECRET ile HS512 imzalanır (alg başlığı sıkıca HS512 olmalı — alg: none ve algoritma-karıştırma reddedilir). exp geçerli, iat son 5 dk içinde olmalıdır. Token bir restoran iddiası taşıyorsa (sub/restaurantId/chainId/…) bu, URL’deki :remoteId ile eşleşmelidir (tenant’lar arası tekrar saldırısına karşı).
Getir ve Migros gelen webhook kullanmaz — siparişleri biz yoklarız, dolayısıyla bu platformlar için yapılandıracağınız bir webhook URL’si veya imza sırrı yoktur.
Menü / uygunluk / dükkân-durum senkronu (giden)
GET /delivery-platforms/menu-mappings?platform=
POST /delivery-platforms/menu-mappings { productId, platform, externalItemId, externalData? }
DELETE /delivery-platforms/menu-mappings/:id
POST /delivery-platforms/menu-sync/:platform (ADMIN)
POST /delivery-platforms/configs/:platform/toggle-restaurant { open: boolean }
POST /delivery-platforms/configs/:platform/test (bağlantı testi)- Menü eşleme: her ürün, platformdaki karşılığına (
externalItemId) bağlanır. Menü senkronu yalnızca eşlenmiş ürünleri gönderir; ad, fiyat (₺) ve uygunluk (isAvailable && isActive) yazılır. - Uygunluk: tek bir kalem
updateItemAvailabilityile açılıp kapatılabilir. - Dükkân aç/kapat:
toggle-restaurant, restoranı yalnızca ilgili platform tarafında açık/kapalı işaretler — yoğunlukta yeni sipariş almayı durdurmak için (entegrasyonu kapatmadan).
Mutabakat (reconciliation)
DeliveryReconciliationService düşük frekanslı, salt-okunur bir drift taramasıdır. Hiçbir adaptör mutabakat raporu sunmadığından platform-tarafı sayımları karşılaştırılmaz; bunun yerine kendi durumumuzdan:
- Bayatlık: etkin bir yoklama yapılandırmasının
lastOrderPollAt’i 1 saatten eskiyse (genelde devre kesici tripi veya token ölümü) işaretlenir; menü senkronu 7 günden eski ise bilgilendirme amaçlı işaretlenir. - Sayım driftı: son 24 saatte ingest edilen teslimat siparişleri ve
externalOrderId’si olmayanlar (platforma geri senkronlanamaz).
Bulgular loglanır ve tek bir delivery.reconciliation.v1 outbox özetine toplanır.
Sorun giderme
- Devre kesici / otomatik kapanma: bir yapılandırmada hata sayısı 10’a ulaşırsa entegrasyon otomatik devre dışı bırakılır (platformu ve log tablosunu spam’lemeyi durdurur).
delivery.platform.auto_disabled.v1olayı yayınlanır. Operatör nedeni (lastError) düzeltip yapılandırmayı elle yeniden etkinleştirmelidir. Kimlik bilgisini güncellemek hata sayacını sıfırlar. - Ölü-mektup kuyruğu (DLQ): başarısız giden işlemler üstel geri çekilmeyle yeniden denenir (varsayılan 3 deneme, en fazla 1 saat). Denemeler tükenince satır DLQ terminal durumuna düşer (
success:false AND nextRetryAt:null AND retryCount>=maxRetries). DLQ derinliğidelivery_dlq_depthmetriğiyle izlenir; satırlar operatör/superadmin tarafından yeniden kuyruğa alınabilir. restaurant not configured: webhook, etkin bir yapılandırmaya eşlenmeyen birremoteIdile geldi —remoteRestaurantId’yi ve entegrasyonun açık olduğunu doğrulayın.- PII maskeleme: gelen ham gövdelerdeki kişisel veriler (telefon/e-posta/adres/ad) hem logdan hem siparişin
externalDatablob’undan kalıcı saklama öncesi maskelenir.
İlgili sayfalar
- Operatör kurulumu: Online Sipariş
- Mutfak akışı: KDS (Mutfak Ekranı)
- Menü kurulumu: Menü Yönetimi