RazGazRazGaz
עבריתEN

מסמך אינטגרציה למערכת ניהול מסעדה

RazGazממשק המסעדה

החוזה המלא לניהול משלוחי RazGaz ממערכת המסעדה עצמה, במקום מאפליקציית המסעדה. כל מה שהאפליקציה עושה נעשה כאן.

גרסה 1.2 · 19.08.2026בסיסapi.raz-gaz.com
תוכן העניינים

1סקירה

אפליקציית המסעדה של RazGaz היא לקוח ולא שרת: היא פותחת הזמנות, קוראת את לוח ההזמנות ומעדכנת אותן דרך ממשק ציבורי, ולאותו ממשק בדיוק יכולה לפנות מערכת הניהול של המסעדה. אינטגרציה לפי המסמך הזה מחליפה את האפליקציה במלואה — אין פעולה שהאפליקציה יכולה לבצע ומערכת המסעדה אינה יכולה.

הזדהותחשבון המסעדה — דואר אלקטרוני וסיסמה, מומרים לאסימון
קריאות RazGazhttps://api.raz-gaz.com
קריאת ועדכון הזמנותhttps://firestore.googleapis.com/v1/projects/razgaz-f7295/databases/(default)/documents
תעבורהHTTPS בלבד. ‏Content-Type: application/json, ‏UTF-8

שני המאחזים הם חלק אחד מהממשק. פתיחת הזמנה ומענה על זמן הכנה הן קריאות ל‑RazGaz; קריאת לוח ההזמנות והפעולות על הזמנה קיימת נעשות ישירות מול מסד הנתונים, כשההרשאה נגזרת מאותו אסימון עצמו. מסעדה רואה ומעדכנת את ההזמנות שלה בלבד — ההגבלה נאכפת בצד השרת, לא בצד הלקוח.

אין ערוץ חוזר יזום. ‏RazGaz אינה קוראת חזרה למערכת המסעדה ואינה שולחת webhook. כדי לדעת מה קרה להזמנה — מי אסף אותה, איפה השליח, האם נמסרה — יש לתשאל, §6.


2מה RazGaz מנפיקה לכם

שלושה דברים, כולם מ‑RazGaz, לפני ההזמנה הראשונה:

מה זה
דואר אלקטרוני וסיסמהחשבון המסעדה. חשבון אחד לכל סניף — אין חשבון אחד שמנהל כמה סניפים, ולכן מערכת שמשרתת שני סניפים מחזיקה שני חשבונות ומזדהה בנפרד לכל אחד
מפתח Web APIמזהה הפרויקט הציבורי הדרוש להמרת הסיסמה לאסימון (§3). אינו סוד ואינו מעניק דבר בלעדיו — הסיסמה היא הסוד
שם הסניףהשם הקנוני של הסניף אצל RazGaz, בדיוק כפי שהוא רשום. זהו הערך שההזמנות שלכם נושאות בשדה restaurantName, והוא מה שמסננים לפיו ב‑§6

אין צורך בקוד סניף ואין צורך במפתח API כמו שיש למערכת קופה: בממשק הזה הזהות היא החשבון, וממנו נגזר הסניף. סניף לא פעיל — מסומן כך על ידי RazGaz — אינו יכול לפתוח הזמנות (403), אך הזמנות שכבר קיימות ממשיכות להיות קריאות ועדכניות.


3הזדהות

3.1קבלת אסימון

POST https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=<WEB_API_KEY>

{
  "email": "branch@example.com",
  "password": "…",
  "returnSecureToken": true
}

בתשובה, שלושת השדות שיש לשמור:

שדהמה לעשות בו
idTokenהאסימון עצמו. נשלח בכל בקשה, §3.3
refreshTokenמחליף אסימון שפג, §3.2. אינו פג מעצמו
expiresInתוחלת החיים בשניות — 3600, שעה אחת

3.2רענון

POST https://securetoken.googleapis.com/v1/token?key=<WEB_API_KEY> עם grant_type=refresh_token&refresh_token=<refreshToken>, בקידוד application/x-www-form-urlencoded. התשובה מכילה id_token חדש.

אין להזדהות מחדש בסיסמה לכל בקשה. יש להחזיק את האסימון בזיכרון ולרענן אותו לפני שהוא פג — כדקה מראש הוא מרווח סביר. אסימון שפג נענה ב‑401, ופנייה חוזרת עם אותו אסימון תיענה באותו אופן בלי סוף.

3.3שימוש

בכל בקשה, לשני המאחזים גם יחד:

Authorization: Bearer <idToken>

בקשה בלי אסימון, עם אסימון שפג או עם אסימון של חשבון אחר נענית ב‑401 או ב‑403 ודבר אינו נרשם — לא נפתחת הזמנה ולא מתעדכן דבר. שגיאה כזו בייצור פירושה שהאינטגרציה שקטה, ויש להסלים אותה, לא לחזור על הבקשה.


4פתיחת הזמנה

POST /createRestaurantOrder

פותחת הזמנת משלוח מהסניף שאליו החשבון משויך. נקודת האיסוף אינה נשלחת ואינה נשלטת: היא המיקום השמור של הסניף אצל RazGaz. ‏RazGaz מחשבת את שכר השליח, משדרת את ההזמנה לשליחים באזור ומנהלת את השליח משלב השיוך ועד המסירה.

4.1בקשה — השדה היחיד שהוא חובה

שדהטיפוסתיאור
deliveryAddressמחרוזתכתובת המסירה בשורה אחת קריאה: רחוב, מספר בית ועיר. אינה משמשת לניווט — הקואורדינטות משמשות לכך — אבל זה מה שהשליח קורא. פרטי הכניסה — קומה, דירה, כניסה — אינם שייכים לשורה הזו אלא לשדות שלהם ב‑§4.3: כשאין קואורדינטות, השורה הזו היא מה שנשלח לאיתור על המפה, וקומה בתוכה פוגעת בדיוק האיתור. קוד שער וכיוצא בזה — ב‑notes

זה השדה היחיד שהוא חובה. כתובת היא כל מה שנקודת הקצה צריכה; כל שאר פרטי ההזמנה, ובכללם רשימת הפריטים והקואורדינטות, אופציונליים.

4.2בקשה — נקודת המסירה על המפה

שדהטיפוסתיאור
deliveryLocationאובייקטנקודת המסירה על המפה. ‏{ "latitude": number, "longitude": number }, שני הערכים מספרים ב‑JSON ולא מחרוזות

מומלץ לשלוח, אך אינו חובה. אם המערכת שלכם מחזיקה קואורדינטות — שלחו אותן, ואנחנו נשתמש בהן כפי שהן. זו התוצאה הטובה ביותר: נקודה שהמערכת שלכם אישרה עדיפה על נקודה שאנחנו מסיקים.

בלעדיהן אנחנו מאתרים את הכתובת אצלנו, וממלאים את העיר של הסניף כשהכתובת לא ציינה עיר — "הרצל 5" מסניף ברעננה הוא הרצל שברעננה. הכתובת שתישמר על ההזמנה תכלול את שם העיר שנוסף (המילים שלכם נשמרות, שם העיר מתווסף בסופן), כדי שהשליח יראה כתובת שלמה.

שני דברים שכדאי לתכנן סביבם:

  • כתובת שאי אפשר לאתר בכלל נדחית ב‑400 עם הודעה בעברית.
  • כתובת שאותרה רק ברמת העיר תקבל נקודה במרכז העיר ולא בבניין, וההזמנה תיפתח כרגיל. אין בתשובה שדה שמבדיל בין השניים — אם הדיוק חשוב לכם, שלחו קואורדינטות.

4.3בקשה — שדות אופציונליים

שדהטיפוסתיאור
itemsמערךפריטי ההזמנה, כל אחד { "name": string, "qty": number }. יש לשלוח כאשר הם ידועים — השליח רואה את הרשימה והיא מה שנבדק מולו באיסוף מהדלפק. אינו חובה: הזמנה שנשלחה בלעדיהם, או עם מערך ריק, נפתחת כרגיל ומוצגת בלי רשימת פריטים — כך מגיעות ממילא הזמנות ממערכות קופה שאינן שולחות רשימה. ערך שאינו מערך נדחה ב‑400
estimatedReadyMinutesמספר שלםדקות מעכשיו ועד שהאוכל יהיה מוכן. גדול מ‑0 ולא יותר מ‑240. יש לשלוח בכל מקרה שבו המערכת יודעת את הזמן — ראו §5. בהזמנה מתוזמנת (scheduledFor שלהלן) יש לשלוח תמיד: ההזמנה משוחררת ישירות לשידור ואיש אינו נשאל שוב, כך שהזמנה מתוזמנת בלעדיו יוצאת בהצהרה שהאוכל כבר מוכן. הוא אינו משפיע על מועד השידור — הוא קבוע, 20 דקות לפני מועד האיסוף
scheduledForמספר | מחרוזתהזמנה מתוזמנת. המועד שבו השליח יגיע למסעדה לאסוף את ההזמנה — אלפיות שנייה מ‑epoch, או ISO‑8601. ההזמנה מוחזקת ומשוחררת בזמן שמאפשר להכין אותה עד שהשליח בדלפק, ואינה מוצעת לאף שליח לפני כן. חייב להיות בעתיד ולא יותר מ‑7 ימים מראש, אחרת הקריאה נדחית ב‑400
customerNameמחרוזתשם הלקוח, כפי שהשליח צריך לבקש אותו
customerPhoneמחרוזתמספר שניתן להתקשר אליו. השליח מתקשר בהגעה
floorמחרוזתקומה. מוצגת לשליח בדלת, בנפרד משורת הכתובת
apartmentמחרוזתדירה
entranceמחרוזתכניסה
notesמחרוזתטקסט חופשי שמוצג לשליח. הוראות מסירה, "להשאיר בדלת"
orderAmountמספרהסכום שהלקוח משלם על ההזמנה, בשקלים
paymentMethodמחרוזת"cash" כאשר על השליח לגבות תשלום מהלקוח. כל ערך אחר, וכן היעדר השדה, נרשמים כ‑"card" — כלומר השליח אינו גובה דבר

items נשמר כפי שנשלח, וכל המסכים — של השליח, של המוקד — קוראים בדיוק את שני המפתחות name ו‑qty. פריט שקורא לכמות שלו בשם אחר יוצג בכמות 1.

שלושת שדות הכניסה (floor, apartment, entrance) נשמרים בנפרד משורת הכתובת ומוצגים לשליח כשהוא מגיע לבניין. ערך "0" נקרא כ"לא נמסר" ואינו מוצג — כך מגיעות הזמנות ממערכות קופה ששולחות "0" לבית פרטי, ואי אפשר להבחין בין השתיים. קומת קרקע שחשוב שתוצג — לכתוב במילים ("קרקע").

4.4שדות שמורים — אין לשלוח

נקודת האיסוף, שכר השליח, רדיוס השידור, אזור התמחור ומספר ההזמנה האנושי כולם נקבעים בצד RazGaz ואינם מתקבלים מהבקשה.

4.5תשובה

HTTPגוףמשמעות
201{ "success": true, "orderId": "…" }ההזמנה נפתחה

orderId הוא מזהה ההזמנה בצד RazGaz, והוא הערך שכל שאר המסמך הזה מקבל: §5, §6.2 ו‑§8 כולם מזוהים לפיו. יש לשמור אותו לצד ההזמנה במערכת המסעדה — אין דרך אחרת להצביע על הזמנה, ואין חיפוש לפי מזהה שלכם.

4.6שגיאות

HTTPמתי
400חסר deliveryAddress, ‏items נשלח כערך שאינו מערך, או estimatedReadyMinutes מחוץ לתחום
400 (עברית)נשלחה כתובת בלי קואורדינטות ולא הצלחנו לאתר אותה על המפה בכלל (§4.2)
400לסניף אין מיקום איסוף שמור אצל RazGaz — תקלת הגדרה, יש לפנות אלינו
401אסימון חסר, פג או פסול (§3)
403לחשבון לא משויך סניף, או שהסניף מסומן כלא פעיל
405לא POST

4.7שליחה חוזרת פותחת הזמנה נוספת

בקריאה הזו אין idempotency. מזהה ההזמנה נוצר בצד RazGaz בכל קריאה, ואין שדה שבו נשלח המזהה שלכם, ולכן אין ל‑RazGaz דרך לזהות ששתי בקשות מתארות את אותה הזמנה. שליחה חוזרת אחרי timeout שבו הבקשה הראשונה כן הגיעה פותחת משלוח שני, ושני שליחים יגיעו לאותו סניף.

לכן: ניסיון חוזר אוטומטי על קריאה זו הוא באחריות מערכת המסעדה, ותנאי לו הוא לדעת שהראשון לא נכתב. הדרך לבדוק היא §6.1 — תשאול ההזמנות הפעילות וחיפוש הזמנה שמתאימה לכתובת, לטלפון ולזמן. הזמנה שנפתחה בטעות פעמיים מבוטלת לפי §8.3.


5זמן הכנה

זמן ההכנה הוא מה שמאפשר ל‑RazGaz לתזמן את השליח כך שיגיע לאוכל מוכן ולא לפניו. לכל סניף רשום אצלנו זמן הכנה ממוצע, או סימון שאומר שהוא נותן זמן לכל הזמנה בנפרד. שני מצבים אפשריים לכן להזמנה חדשה:

  • נשלח estimatedReadyMinutes ב‑§4.1, או שלסניף יש ממוצע רשום — ההזמנה יוצאת לשידור מיד, וזה המסלול הרצוי.
  • לא נשלח, והסניף מסומן כמי שנותן זמן לכל הזמנה — ההזמנה מוחזקת בסטטוס awaiting_prep_time ואינה משודרת לאף שליח עד שנענית לפי §5.1 או §5.2. הזמנה שאינה נענית נשארת מוחזקת: אין שחרור אוטומטי, כי שידור על סמך ניחוש גרוע יותר מהמתנה. ‏RazGaz מקבלת התראה פנימית על הזמנה שאיש אינו עונה עליה, אבל היא אינה משחררת אותה.

הדרך הפשוטה להימנע מהמצב השני היא לשלוח estimatedReadyMinutes תמיד.

5.1"מוכן בעוד N דקות"

POST /submitPrepTime

{ "orderId": "…", "minutes": 20 }

minutes — מספר דקות מעכשיו, גדול מ‑0 ולא יותר מ‑240.

הקריאה עושה אחד משני דברים, לפי מצב ההזמנה, ואין צורך לבחור ביניהם:

מצב ההזמנהמה קורה
awaiting_prep_timeמשחרר את ההזמנה: הסטטוס עובר ל‑pending ושעון ההכנה מתחיל מעכשיו. אם מנהל RazGaz הקפיא את פרסום הסניף, ההזמנה עוברת ל‑held במקום, ומוקדן מפרסם אותה — אין מה לעשות מצדכם
כל מצב פעיל אחרעדכון זמן ההכנה. הסטטוס אינו נוגע; זה המסלול של "האוכל יתאחר בעוד עשר דקות", והשליח שכבר שויך מקבל על כך הודעה
delivered / cancelled409 — ההזמנה נגמרה

תשובה: { "success": true, "orderId": "…", "minutes": 20, "released": true, "readyAt": 1755513600000 }. ‏released אומר אם זו הייתה השחרור או העדכון; readyAt — אלפיות שנייה מ‑epoch — מגיע רק בשחרור.

5.2הזמנה למועד מאוחר

אם המועד ידוע כבר בפתיחת ההזמנה, שלחו scheduledFor ב‑createRestaurantOrder (§4.3) וזה הכול — ההזמנה מתוזמנת מלכתחילה ואין צורך בקריאה הזו. מה שלהלן נוגע להזמנה שכבר פתוחה: אחת שנשלחה בלי מועד, או אחת שממתינה לזמן הכנה.

POST /scheduleRestaurantOrder

{ "orderId": "…", "scheduledFor": "2026-08-18T20:30:00+03:00", "minutes": 25 }

התשובה השנייה לאותה שאלה: לא כמה זמן האוכל לוקח מעכשיו אלא מתי שהשליח יבוא לאסוף אותו. ‏RazGaz מחזיקה את ההזמנה ומשחררת אותה לשידור בעצמה, בזמן שמחושב אחורה מהמועד — דרך זמן ההכנה, כך שהאוכל מוכן כשהשליח בדלפק. לכן minutes נדרש כאן גם הוא: זמן ההכנה הוא חלק מהחשבון, ובלעדיו המועד אינו ניתן לחישוב.

שדהטיפוסתיאור
orderIdמחרוזתההזמנה. חייבת להיות ב‑awaiting_prep_time
scheduledForמספר | מחרוזתמועד האיסוף המבוקש — השעה שבה השליח יגיע למסעדה: אלפיות שנייה מ‑epoch, או ISO‑8601. חייב להיות בעתיד ולא יותר מ‑7 ימים מראש
minutesמספר שלםזמן ההכנה, 1–240

תשובה: { "success": true, "orderId": "…", "scheduledFor": …, "releaseAt": …, "minutes": 25 } — שתי החותמות באלפיות שנייה. ‏releaseAt הוא הרגע שבו ההזמנה תצא לשידור, ועד אז היא בסטטוס scheduled ואינה מוצעת לאף שליח.

HTTPמתי
400מועד בעבר, מועד רחוק מ‑7 ימים, או minutes מחוץ לתחום
404הזמנה לא מוכרת
403ההזמנה אינה של הסניף הזה
409ההזמנה אינה ב‑awaiting_prep_time — כלומר היא כבר שוחררה, ותזמון אחורה אינו אפשרי מכאן

הזמנה שכבר יצאה לשידור אינה ניתנת לתזמון מהממשק הזה. אם לקוח הזיז מועד לאחר שההזמנה שוחררה — יש לבטל (§8.3) ולפתוח חדשה בזמנה.


6קריאת ההזמנות

כאן נקרא כל מה שהאפליקציה מציגה: הסטטוס, השליח, מקומו על המפה, זמני הדרך. הקריאה היא מול המאחז השני, firestore.googleapis.com, עם אותו אסימון מ‑§3.3. הבסיס לכל הקריאות בפרק הזה:

https://firestore.googleapis.com/v1/projects/razgaz-f7295/databases/(default)/documents

מגבלות התשאול הן חלק מהחוזה. התשאול הוא הדרך היחידה לדעת מה קרה להזמנה, ולכן הקצב שלו אינו עניין של נימוס:

קצב מותרבקשה אחת ל‑דקה לכל סניף, לכל היותר
כשאין הזמנה פעילהלא לתשאל כלל
מספר המתשאליםאחד לכל סניף — לא אחד לכל קופה או מסך

שלושת אלה נאמרים במפורש מפני שכל אחד מהם כבר נשבר אצלנו בייצור, באינטגרציה אחרת: מערכת קופה אחת מתשאלת נקודת קצה אחרת שלנו פעם ב‑0.37 שניות — פי 54 מהקצב שנמסר לה — סביב השעון, כולל בשעות שבהן המטבח סגור, ומכמה עמדות במקביל שכל אחת מחזיקה שעון משלה. היא עשתה זאת בעבור מסעדה אחת ששלחה שמונה הזמנות.

שום דבר אינו אוכף את הקצב הזה בכל בקשה. אף קריאה שלכם לא תידחה בגלל חריגה — נתיב הקריאה הוא שירות של Google ואין בינו לביניכם שום רכיב שלנו. מה שאנחנו רואים ביומיום הוא נפח הקריאות הכולל מכל הסניפים; ייחוס של קפיצה לסניף מסוים הוא דבר שאנחנו מפעילים כשקפיצה כזו כבר מולנו, והוא נוקב בשמכם. חריגה מתמשכת פירושה פנייה אליכם, ואם היא נמשכת, השבתת חשבון הסניף: זה הכלי היחיד שיש לנו כאן, והוא גם מפסיק את פתיחת ההזמנות. עדיף להימנע מהשיחה הזו — ובוודאי שעדיף לא להיזקק לתשאול כלל, שזה בדיוק מה שמאזין (§6.6) עושה.

6.1ההזמנות הפעילות

POST …/documents:runQuery

{
  "structuredQuery": {
    "from": [{ "collectionId": "orders" }],
    "where": { "compositeFilter": { "op": "AND", "filters": [
      { "fieldFilter": {
        "field": { "fieldPath": "restaurantName" },
        "op": "EQUAL",
        "value": { "stringValue": "שם הסניף" } } },
      { "fieldFilter": {
        "field": { "fieldPath": "status" },
        "op": "IN",
        "value": { "arrayValue": { "values": [
          { "stringValue": "scheduled" },
          { "stringValue": "awaiting_prep_time" },
          { "stringValue": "held" },
          { "stringValue": "pending" },
          { "stringValue": "broadcasting" },
          { "stringValue": "accepted" },
          { "stringValue": "arrived_at_pickup" },
          { "stringValue": "picked_up" },
          { "stringValue": "arrived_at_delivery" }
        ] } } } }
    ] } }
  }
}

שם הסניף הוא השם הקנוני מ‑§2, זהות מלאה תו בתו. שם שאינו זהה מחזיר קבוצה ריקה ולא שגיאה — זו התקלה השקטה היחידה בפרק הזה, ולכן כדאי לוודא את השם מול הזמנה אחת אמיתית לפני העלייה לאוויר.

6.2הזמנה אחת

GET …/documents/orders/<orderId> — עם האסימון בכותרת. ‏404 להזמנה לא מוכרת, ‏403 להזמנה של סניף אחר.

זו הדרך לעקוב אחרי הזמנה מסוימת בלי לתשאל את כל הלוח, ובפרט הדרך לוודא שקריאה מ‑§4 או §5 שאין ממנה תשובה אכן נכתבה.

6.3היסטוריה

אותה שאילתה כמו §6.1 עם "stringValue": "delivered" ו‑"cancelled" ברשימת הסטטוסים. אין מחיקה של הזמנות: הזמנה שנמסרה או בוטלה נשארת קריאה.

6.4השדות שחוזרים

התשובה היא מסמך Firestore, כלומר כל ערך עטוף בטיפוס שלו (stringValue, integerValue, doubleValue, timestampValue, nullValue, geoPointValue, mapValue, arrayValue). אלה השדות שיש בהם עניין למערכת המסעדה; יש בהזמנה שדות נוספים, כולם תפעול פנימי של RazGaz:

שדהמה זה
statusמצב ההזמנה, §7
platformOrderRefמספר ההזמנה האנושי — מה שאומרים בטלפון
estimatedReadyMinutesזמן ההכנה שנרשם, בדקות
readyAtהרגע שבו האוכל צפוי להיות מוכן, לפי זמן ההכנה
markedReadyAtהרגע שבו נאמר שהאוכל מוכן. ריק כל עוד לא נאמר — §8.1
carrierIdמזהה השליח, או null כל עוד לא שויך. המפתח ל‑§6.5
estimatedArrivalהערכת RazGaz לזמן ההגעה של השליח
scheduledForמועד האיסוף בהזמנה מתוזמנת — השעה שבה השליח יגיע למסעדה, §5.2
cancelledAt, cancellationReasonביטול: מתי, ומה נכתב עליו
deliveredAtרגע המסירה ללקוח
deliveryAddress, customerName, customerPhone, items, orderAmount, paymentMethod, notesכפי שנשלחו ב‑§4

6.5מעקב אחר השליח

GET …/documents/carriers/<carrierId> — כאשר ההזמנה כבר נושאת carrierId.

שדהמה זה
displayNameשם השליח
phoneהטלפון שלו — למי שרוצה כפתור התקשרות
currentLocationמקומו על המפה, geoPointValue, מתעדכן כל עוד הוא בפעילות
vehicleTypeסוג הרכב

זהו המידע שהאפליקציה מציירת על המפה. מסמך השליח נקרא במלואו, וכל מה שאינו בטבלה הזו הוא תפעול פנימי של RazGaz ואינו חלק מהחוזה הזה — אין להסתמך עליו.

6.6לא לתשאל בכלל — הדרך המומלצת

ל‑Firestore יש ערוץ הקשבה: פותחים חיבור אחד, ומקבלים כל שינוי ברגע שהוא קורה. זו הדרך שאפליקציית המסעדה עצמה עובדת, וזו הדרך המומלצת כאן — היא מגיעה מיד במקום בתוך דקה, ואין לה שום עלות בזמן שדבר אינו קורה, כך שגם המגבלות שלמעלה פשוט אינן חלות.

היא דורשת את ה‑SDK של Firebase, כי ערוץ ההקשבה הוא gRPC ואינו קיים ב‑REST. ב‑Node:

const { initializeApp } = require('firebase/app');
const { getAuth, signInWithEmailAndPassword } = require('firebase/auth');
const { getFirestore, collection, query, where, onSnapshot } = require('firebase/firestore');

const app = initializeApp({ apiKey: '<WEB_API_KEY>', projectId: 'razgaz-f7295' });
await signInWithEmailAndPassword(getAuth(app), 'branch@example.com', '…');

onSnapshot(
  query(
    collection(getFirestore(app), 'orders'),
    where('restaurantName', '==', 'שם הסניף'),
    where('status', 'in', ['scheduled', 'awaiting_prep_time', 'held', 'pending', 'broadcasting',
       'accepted', 'arrived_at_pickup', 'picked_up', 'arrived_at_delivery']),
  ),
  (snap) => {
    for (const change of snap.docChanges()) {
      // 'added' — הזמנה נכנסה ללוח · 'modified' — סטטוס, שליח או ETA השתנו
      console.log(change.type, change.doc.id, change.doc.data().status);
    }
  },
);

השאילתה זהה לזו של §6.1, וכך גם ההרשאות. ‏signInWithEmailAndPassword מחזיק את האסימון בעצמו ומרענן אותו — §3.2 אינו נחוץ במסלול הזה. השדות שחוזרים הם אותם שדות של §6.4, אלא שהם מגיעים כערכים רגילים ולא עטופים בטיפוסי Firestore.

מה שמגיע הוא כל שינוי במסמך, לא רק שינוי סטטוס — גם עדכוני זמן ההגעה של השליח, שנכתבים כל שתי דקות כל עוד ההזמנה בדרך. זה מה שמאפשר להציג ETA שזז על המסך, וזה גם מה שקובע את העלות: כמה עשרות שינויים לכל הזמנה, ואפס בין הזמנות.

מתי בכל זאת לתשאל. כשאין SDK — מערכת ב‑PHP או ב‑‎.NET ישן שאינה יכולה להחזיק חיבור — התשאול של §6.1 הוא המסלול, בכפוף למגבלות שבראש §6.


7סטטוסים

statusמשמעות
awaiting_prep_timeמוחזקת: ‏RazGaz מחכה לזמן הכנה. אינה משודרת לאף שליח — §5
heldמוחזקת אצל מוקדן RazGaz: הסניף מוגדר לפרסום ידני, ומוקדן משחרר כל הזמנה. אינה משודרת לאף שליח; אין מה לעשות מצד הסניף — מבשלים כרגיל
scheduledנקבעה למועד מאוחר, ממתינה לשחרור אוטומטי — §5.2
pendingשוחררה, טרם החל שידור
broadcastingמוצעת לשליחים באזור
acceptedשליח לקח אותה על עצמו ובדרך לסניף
arrived_at_pickupהשליח בסניף
picked_upהאוכל אצל השליח, בדרך ללקוח
arrived_at_deliveryהשליח אצל הלקוח
deliveredנמסרה. סופי
cancelledבוטלה. סופי

הסטטוס מתקדם בסדר הזה, אבל לא כל מצב מופיע בהכרח בתשאול: שני מצבים סמוכים יכולים להתחלף בין שתי דגימות. יש להתייחס לסטטוס כמצב נוכחי ולא כאירוע, ולא לבנות לוגיקה שדורשת לראות כל מצב.


8פעולות על הזמנה קיימת

שלוש הפעולות שהאפליקציה מציעה על כרטיס הזמנה. כולן POST …/documents:commit מול המאחז של §6, עם אותו אסימון, ובצורה מדויקת: הצורות שלהלן הן מה שמותר, וכתיבה אחרת על הזמנה נדחית בצד השרת — שדה שאינו ברשימה, או הזמנה של סניף אחר, מוחזרים כשגיאת הרשאה ולא כתיבה חלקית.

בכל הבקשות <orderId> הוא המזהה מ‑§4.5, ו‑name הוא הנתיב המלא projects/razgaz-f7295/databases/(default)/documents/orders/<orderId>.

8.1האוכל מוכן

{ "writes": [ {
  "update": {
    "name": "projects/razgaz-f7295/databases/(default)/documents/orders/<orderId>",
    "fields": { "readySource": { "nullValue": null } }
  },
  "updateMask": { "fieldPaths": ["markedReadyAt", "readySource"] },
  "updateTransforms": [
    { "fieldPath": "markedReadyAt", "setToServerValue": "REQUEST_TIME" }
  ]
} ] }

זו ההודעה לשליח שאין טעם להמתין. markedReadyAt נכתב כזמן השרת — לא כשעון של מערכת המסעדה — כי כל הצד השני קורא אותו כזמן RazGaz, ושעון שנמצא כמה דקות מהמקום מזיז את ההודעה. ‏readySource: null נשלח באותה כתיבה; השדה מתעד מי אמר שהאוכל מוכן, ומ-19 באוגוסט 2026 התשובה היא תמיד אדם.

שום דבר אחר לא כותב את השדה הזה. זמן הכנה הוא הערכה, ולתת להערכה שפגה לסמן "מוכן" פירושו לומר לשליח שהאוכל מוכן בלי שאיש הסתכל עליו. לכן הערכה שנגמרה אינה משנה דבר בהזמנה: אם הקריאה הזו לא נעשית, פשוט לא נאמר לשליח דבר, לאורך כל חיי ההזמנה. ההזמנה עדיין משודרת, נלקחת ונמסרת כרגיל. לשלוח כשהשקית על הדלפק, לא לפני.

לקרוא פעם אחת. קריאה שנייה כותבת חותמת חדשה על הראשונה ואינה מוסיפה דבר.

8.2עדכון זמן הכנה

לא כאן — §5.1. submitPrepTime נכון לשני המצבים, כותב את אותו השדה, וגם מודיע לשליח שכבר שויך. כתיבה ישירה של estimatedReadyMinutes אפשרית ואינה מודיעה לאיש; אין סיבה להעדיף אותה.

8.3ביטול

{ "writes": [ {
  "update": {
    "name": "projects/razgaz-f7295/databases/(default)/documents/orders/<orderId>",
    "fields": {
      "status": { "stringValue": "cancelled" },
      "cancellationReason": { "stringValue": "בוטל על ידי המסעדה" }
    }
  },
  "updateMask": { "fieldPaths": ["status", "cancelledAt", "cancellationReason"] },
  "updateTransforms": [
    { "fieldPath": "cancelledAt", "setToServerValue": "REQUEST_TIME" }
  ]
} ] }

"cancelled" הוא הערך היחיד הנתמך ב‑status מהממשק הזה. שאר הסטטוסים הם תיאור של מה שקורה לשליח, ו‑RazGaz היא שכותבת אותם; הזמנה שסומנה מבחוץ במצב שאינו מתאים למה שקורה בפועל מתנתקת ממה שהמוקד והשליח רואים.

cancellationReason נקרא בידי אדם ולכן כדאי שיהיה קונקרטי — "הלקוח ביטל", "הסניף סגר" — ולא ריק.

ביטול הוא סופי ואינו מבטל את עצמו. הזמנה מבוטלת אינה חוזרת לשידור ואין דרך להחיות אותה; מה שנדרש אחריה הוא הזמנה חדשה לפי §4. ביטול הזמנה שהשליח כבר אסף אינו מחזיר את האוכל — במצב picked_up ואחריו יש להתקשר למוקד ולא להסתמך על הקריאה הזו לבד.


9מה שאין בממשק הזה

מה שכדאי לדעת לפני התכנון, כדי שלא יתוכנן סביבו:

קריאה חוזרת ל‑RazGazאין. אין webhook ואין הודעת דחיפה למערכת המסעדה; המידע מגיע בתשאול, §6
תרגום כתובת לקואורדינטותמ‑2026‑08‑25 יש: שלחו כתובת בלי קואורדינטות ואנחנו נאתר אותה, וגם נמלא את העיר של הסניף כשהכתובת לא ציינה עיר (§4.2). שליחת קואורדינטות עדיין מדויקת יותר, והיא הדרך היחידה שמבטיחה בניין ולא מרכז עיר
מזהה הזמנה משלכםאין. המזהה הוא של RazGaz, ואין חיפוש לפי המזהה שלכם — §4.5, §4.7
שינוי כתובת או פרטי לקוח לאחר הפתיחהאין. הזמנה שנפתחה עם כתובת שגויה מבוטלת ונפתחת מחדש
בחירת שליח, או שינוי אזור ותמחוראין. שיוך שליח, שכרו ורדיוס השידור הם של RazGaz
תזמון הזמנה שכבר שוחררהאין. התזמון אפשרי רק מ‑awaiting_prep_time — §5.2
חשבון אחד לכמה סניפיםאין. חשבון לכל סניף, §2

10לפני עלייה לאוויר

בסדר הזה, ועל סניף אחד אמיתי:

  1. הזדהות מחזירה אסימון, והרענון עובד. לא רק ההזדהות — גם המסלול שאחרי שעה, כי בייצור הוא זה שנתקל בו כל הזמן.
  2. הזמנת בדיקה נפתחת ומחזירה 201 ו‑orderId, וה‑orderId נשמר אצלכם לצד ההזמנה.
  3. תשאול §6.1 מחזיר בדיוק את ההזמנה הזאת. אם הוא מחזיר קבוצה ריקה — שם הסניף אינו זהה לשם הקנוני. זו התקלה השכיחה.
  4. הסטטוס זז. מ‑pending ל‑broadcasting ומשם ל‑accepted כששליח לוקח את ההזמנה, ואז carrierId מתמלא ו‑§6.5 מחזיר את מקומו.
  5. "האוכל מוכן" (§8.1) נכתבmarkedReadyAt מתמלא בתשאול הבא.
  6. ביטול (§8.3) עובד על ההזמנה שנפתחה לבדיקה, וסוגר אותה. לסיים בה כדי לא להשאיר משלוח בדיקה פתוח בשידור.
  7. estimatedReadyMinutes נשלח על כל הזמנה — או שאושר מול RazGaz שלסניף יש זמן הכנה ממוצע רשום. אחרת ההזמנה הראשונה בייצור תיתקע מוחזקת, §5.

לשאלות ולתקלות אינטגרציה — הכתובת היא מי שמסר לכם את המסמך הזה. שגיאה שחוזרת בייצור, ובפרט 401, 403 או תשאול שמחזיר ריק, היא אינטגרציה שאינה מספקת דבר בשקט; יש להסלים אותה מיד ולא להמתין להזמנה הבאה.