Fan Yemek ↔ FanPBX · Sürüm 1.1

FanPBX Çağrı Sözleşmesi

Müşteri, restoran ve kurye birbirinin gerçek numarasını görmeden konuşur; platform gerektiğinde kendisi arayıp bilgilendirir. Bu belge iki tarafın neyi yazacağını tanımlar.

docs/fanyemek-telephony-api.yaml 9 uç · OpenAPI 3.0.3 16 Eylül 2026
Fan Yemek yazacak — biz çağırırız FanPBX sağlar — siz çağırırsınız

İstenenler ve karşılıkları

Beş talebin dördü sözleşmede zaten tanımlıydı; eksik olan yalnızca platformun kendi başlattığı bilgilendirme çağrısıydı. 1.1 onu ekliyor.

İstekKarşılığıDurum
Restoranı arayıp "onaylanmayan siparişiniz var" demek POST /pbx/notify 1.1 · yeni
Müşteriyi arayıp iptal bildirmek POST /pbx/notify 1.1 · yeni
Restoran + müşteri numaralarını veren uç POST /partner/order-parties Vardı
Tuşlanınca numaraya çözülen kimlik POST /partner/call-code Vardı — sipariş no değil
Müşteri arayınca açık siparişinin durumu POST /partner/resolve Vardı · enum genişledi

Sipariş numarası kod olarak kullanılamaz

"Numaranın sonuna sipariş id'si ekleyelim" kısmı güvenli değil

Sipariş numaraları kısa, tahmin edilebilir ve çoğu zaman artan sıradadır. IVR'a bağlanıp sırayla numara denemek, rastgele müşterilere bağlanmanın en ucuz yoludur — saldırgan kimseyi tanımak zorunda değil, sadece saymak zorunda.

Bu yüzden tuşlanan kod /partner/call-code ile üretilir ve sipariş numarasından bağımsızdır: kriptografik rastgele, en az 8 hane, siparişe ve role ayrı (restoranın kodu ile kuryeninki farklı), süreli, ve 5 hatalı denemeden sonra iptal.

İhtiyacın karşılanıyor — "bir kimlik tuşlansın, karşılığında doğru tarafa bağlanayım" tam olarak bu ucun işi. Değişen tek şey, o kimliğin sipariş numarası olmaması.

1.1 ile gelen: bilgilendirme çağrısı

POST /pbx/notify FanPBX sağlar yeni

Anons çalan otomatik çağrı başlatır; isteğe bağlı olarak tuş yanıtı toplar.

GET /pbx/notify/{notificationId} FanPBX sağlar yeni

Webhook'u kaçıranlar için durum sorgusu. Webhook asıl kanal; bu onun sağlaması.

İstek

POST /pbx/notify
{
  "orderId": "ord_8412",
  "role": "restaurant",
  "template": "unconfirmed_order",
  "variables": { "pendingCount": 3 },
  "collectResponse": true,
  "dedupeKey": "ord_8412:unconfirmed"
}

→ 202  { "notificationId": "ntf_91c…", "result": "queued" }

Numara göndermiyorsunuz. /pbx/bridge ile aynı kural: istek yalnızca orderId ve role taşır, numarayı FanPBX /partner/order-parties üzerinden kendisi öğrenir. Gerçek numaranın geçtiği kanal sayısını ikiye çıkarmamak bu sözleşmenin tek tasarım ilkesi.

Çağrı kurulmadan önce üç kapı

Bunlar çağıranın insafına bırakılmaz, FanPBX zorunlu olarak uygular:

Tuş yanıtı almadan arama yarım kalır

Restoranı "onayınız bekleniyor" diye arayıp onaylama imkânı vermemek, aramanın yarısını yapmaktır. collectResponse: true ile anonsun sonunda tuş beklenir — 1 onayla, 2 reddet, 0 temsilciye bağlan — ve sonuç /partner/call-events gövdesinde dtmf alanıyla size döner.

Anons metni şablon olarak sabit

template bir enum: unconfirmed_order ve order_cancelled. Serbest metin kabul edilmez. Anons sesi önceden kaydedilmiştir ve ticari ileti/İYS değerlendirmesi şablon bazında yapılmıştır; çağıranın metin göndermesi, o değerlendirmeyi her istekte yeniden yapılması gereken bir şeye çevirirdi.

Sipariş durumu genişledi

CallSession.stage enum'una awaiting_confirmation ve cancelled eklendi.

Bu iki durum olmadan akış kendi kendini yiyordu

İkisi de yeni bildirim akışlarının konusu: restoran onaylanmamış sipariş için aranıyor, müşteri iptal için aranıyor. Enum'da karşılıkları olmadan bu siparişler no_active_order ile aynı kovaya düşerdi — yani tam da aranması gereken müşteri, geri aradığında "kayıtlı siparişiniz bulunamadı" anonsunu duyardı.

stage: awaiting_confirmation | placed | preparing
     | ready | picked_up | delivered | cancelled

Uçların tamamı

POST/partner/resolveFan Yemek yazacak

Gelen aramayı çöz: bu numara kim, hangi siparişleri açık? Gecikme bütçesi 250 ms — arayan hatta bekliyor.

POST/partner/call-codeFan Yemek yazacak

IVR'da tuşlanacak kısa ömürlü kodu üretir. Restorana panelinde, kuryeye uygulamasında gösterilir.

POST/partner/order-partiesFan Yemek yazacak

Bir siparişin taraflarını çözer. Uygulama içi "Ara" ve bildirim çağrısı bunu kullanır.

POST/partner/call-eventsFan Yemek yazacak

Görüşme ve bildirim sonucu buraya düşer — süre, sonuç, tuşlanan hane.

GET/partner/opt-outFan Yemek yazacak

Bu numara aranmayı reddetmiş mi? Her bildirim çağrısından önce sorulur.

POST/pbx/bridgeFanPBX sağlar

Uygulama içi "Ara": önce arayanı arar, açınca karşı tarafı bağlar. İki bacakta da CallerID platform numarasıdır.

POST/pbx/notifyFanPBX sağlaryeni

Bilgilendirme çağrısı.

GET/pbx/notify/{id}FanPBX sağlaryeni

Bildirim durumu.

GET/pbx/recordings/{ref}FanPBX sağlar

Ses kaydı için kısa ömürlü imzalı adres. Kalıcı URL yoktur; her erişim ayrı yetkilendirilir ve denetime yazılır.

Kimlik: X-Api-Key

/pbx/* uçlarına erişim, CRM panelinden üretilen bir anahtarla olur (Ayarlar → API istemcileri). Tek başlık, tek parça değer:

X-Api-Key: fypbx_5e75a6e396f7fdee.<gizli yarı>
           └── açık: arama anahtarı,   └── sunucuda yalnızca
               panelde görünür,            SHA-256 özeti durur
               log'da güvenli

Gizli yarı yalnızca üretildiği anda bir kez gösterilir; geri okutan bir uç yoktur. Karşılaştırma sabit zamanlıdır. Kaybolursa yeni anahtar üretilir, eskisi iptal edilir — iptal satırı silmez, denetim kaydının işaret edeceği bir şey kalması gerekir.

Arayan başına ayrı anahtar

İki yetkiyi tek anahtarda birleştirmeyin

telephony:mask santralin bizi aradığı yüzey — Asterisk dialplan'ından, santralin kendi adresinden gelir. telephony:notify ise Fan Yemek'in bizi aradığı yüzey, onların sunucularından gelir.

Tek anahtarda toplanırsa IP listesi her iki arayanı da kapsayacak kadar gevşer — yani her biri diğerinin adresinden çağrı yapabilir hale gelir — ve birini iptal etmek diğerini de kapatır.

AnahtarYetkiArayan
Santraltelephony:maskFreePBX dialplan
Fan Yemektelephony:notifyFan Yemek sunucuları

Her anahtar isteğe bağlı bir IP listesi tutar. Yetkisiz anahtar kabul edilmez — boş yetki listesi "her şey" değil, "hiçbir şey" anlamına gelir: yarım bırakılmış bir kayıt atıl kalmalı, sessizce tam yetkili olmamalı.

Reddetme her zaman 404

Yanlış sır, yanlış yetki, listede olmayan adres — üçü de ayırt edilemez bir 404 alır. 403 "bu uç var ve bir tahmin uzağındasın" demektir; gerçek telefon numarası çözen bir yüzeyde bu bedava keşiftir.

/partner/* yönü (bizim Fan Yemek'i çağırdığımız uçlar) değişmedi: mTLS + HMAC imza. Gerçek numara taşıyan kanal orasıdır.

Numara planı ve güvenlik

HatNumaraKim ararKime bağlanır
Müşteri hattı+90 216 740 04 55MüşteriKurye yoldaysa kurye, değilse restoran
İş ortağı hattı+90 216 740 04 70Restoran / KuryeSiparişin müşterisi
Gerçek numara sınırı

Gerçek telefon numaraları yalnızca sunucudan-sunucuya /partner/resolve ve /partner/order-parties yanıtlarında, yalnızca o çağrıyı kurmaya yetecek süre için geçer. Hiçbir mobil uygulama, hiçbir panel, hiçbir CDR kaydı ve hiçbir webhook gövdesi karşı tarafın numarasını taşımaz. Bizde kalıcı saklanmaz; kayıtta yalnızca pepper'lı HMAC özeti tutulur.

Taşıma mTLS + HMAC imza ile korunur. Tek başına bearer token yeterli görülmedi: token sızarsa tüm sipariş tabanının telefon rehberi çekilebilir hale gelir. İmzaya X-FY-Timestamp (5 dk replay penceresi) ve X-FY-Nonce eşlik eder.