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.
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.
| İstek | Karşı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ş 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ı.
Anons çalan otomatik çağrı başlatır; isteğe bağlı olarak tuş yanıtı toplar.
Webhook'u kaçıranlar için durum sorgusu. Webhook asıl kanal; bu onun sağlaması.
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.
Bunlar çağıranın insafına bırakılmaz, FanPBX zorunlu olarak uygular:
result: suppressed döner. Tekrar denemeyin.
unconfirmed_order 21:00-09:00
arası ertelenir, order_cancelled 08:00-22:00 penceresinde
çalışır. urgent: true pencereyi geçer ama denetime yazılır —
gece üçte çalan telefonun kimin kararı olduğu sonradan sorulabilmeli.
dedupeKey. Aynı anahtarla 30 dakika içinde
ikinci çağrı kurulmaz. Sipariş kuyruğu bir bildirim döngüsüne girdiğinde
restoranın telefonu kırk kez çalmasın diye; üretimde en sık görülen
bildirim kazası budur. Vermezseniz orderId + template
kullanılır, yani varsayılan zaten korumalıdı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.
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.
CallSession.stage enum'una awaiting_confirmation
ve cancelled eklendi.
İ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
Gelen aramayı çöz: bu numara kim, hangi siparişleri açık? Gecikme bütçesi 250 ms — arayan hatta bekliyor.
IVR'da tuşlanacak kısa ömürlü kodu üretir. Restorana panelinde, kuryeye uygulamasında gösterilir.
Bir siparişin taraflarını çözer. Uygulama içi "Ara" ve bildirim çağrısı bunu kullanır.
Görüşme ve bildirim sonucu buraya düşer — süre, sonuç, tuşlanan hane.
Bu numara aranmayı reddetmiş mi? Her bildirim çağrısından önce sorulur.
Uygulama içi "Ara": önce arayanı arar, açınca karşı tarafı bağlar. İki bacakta da CallerID platform numarasıdır.
Bilgilendirme çağrısı.
Bildirim durumu.
Ses kaydı için kısa ömürlü imzalı adres. Kalıcı URL yoktur; her erişim ayrı yetkilendirilir ve denetime yazılır.
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.
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.
| Anahtar | Yetki | Arayan |
|---|---|---|
| Santral | telephony:mask | FreePBX dialplan |
| Fan Yemek | telephony:notify | Fan 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ı.
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.
| Hat | Numara | Kim arar | Kime bağlanır |
|---|---|---|---|
| Müşteri hattı | +90 216 740 04 55 | Müşteri | Kurye yoldaysa kurye, değilse restoran |
| İş ortağı hattı | +90 216 740 04 70 | Restoran / Kurye | Siparişin müşterisi |
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.