שגיאות
ל-v1 אין מעטפת שגיאה נפרדת. כשל חוזר באותו גוף בעל ארבעה מפתחות כמו הצלחה, כאשר success מתהפך ל-false והסיבה נמצאת ב-message:
{
"data": [],
"message": "Invalid License Key",
"success": false,
"warning": ""
}| Key | סוג | תיאור |
|---|---|---|
data | array/object | מערך JSON ריק ([]) ברוב הכשלים. חלק מנקודות הקצה מכניסות לכאן פרטים — ראו שגיאות ולידציה ואת ההערה על change_order_status למטה. |
message | string | סיבה קריאה-אנוש, לעיתים קרובות הטקסט של חברת השילוח עצמה, לפעמים מתורגם. |
success | bool | false בכשל. האות המחייב. |
warning | string | תמיד קיים, ריק בנקודות הקצה של השילוח ב-v1. |
אין קוד שגיאה קריא-מכונה
v1 קדמה לשדה ה-code היציב שנושאות שגיאות v2. שום דבר בכשל של v1 אינו מתוכנן להתאמה תוכניתית: message הוא טקסט חופשי והוא עשוי להשתנות או להגיע מתורגם. הסתעפו לפי סטטוס ה-HTTP בתוספת success, והתייחסו ל-message כמשהו לרישום ביומן או להצגה לאדם.
success: false אינו מרמז על סטטוס שאינו 2xx
כמה מסלולים ב-v1 מחזירים מעטפת כשל בסטטוס HTTP 200 שהוא ברירת המחדל. המקרה הברור ביותר הוא change_order_status: כאשר קוד המעקב לא נמצא תחת הרישיון הקורא, הוא מחזיר 200 עם success: false ועם message: "Something went wrong". לקוח שבודק רק את response.ok יקרא את זה כביטול שהצליח. בדקו תמיד את success.
סטטוסי HTTP שצריך לטפל בהם
| Status | מתי זה קורה | ניסיון חוזר? |
|---|---|---|
200 | הצלחה — וגם חלק מהכשלים המטופלים (ראו האזהרה למעלה). בדקו success. | — |
400 | נדחה לפני שחברת השילוח נקראה: מכסת חבילות המשלוח של החשבון מוצתה, סטטוס ההזמנה מוחרג מיצירת משלוח על ידי מסנן סטטוסי ההזמנה של הרישיון, הפעולה אינה נתמכת על ידי חברת השילוח של הרישיון הזה, או שחיפוש הצעת מחיר / משלוח אקספרס נכשל. | לא — התנאי נוגע לחשבון או לבקשה. |
403 | בדיקת הרישיון נכשלה. מפתח לא מוכר, רישיון שבוטל, או שער פרימיום. ראו אימות. | לא. |
404 | הדבר שאליו פנו אינו קיים תחת הרישיון הקורא. שני מבנים: המעטפת (order-label, למשל Order not found, No active shippings found for this order) והגוף החשוף של Laravel {"message": "..."} עבור נקודות הקצה ששולפות עם firstOrFail (get_order_details, get_shipping_status, label). | לא. |
409 | open_new_order בלבד. בקשה אחרת עבור אותו מפתח רישיון ואותו מזהה הזמנה נמצאת בתהליך והחזיקה את המנעול לכל אורך החלון שלה. message מבקש מהקורא לנסות שוב. | כן, לאחר השהיה — אבל ראו קודם את ההערה על קריאה חוזרת (replay) למטה. |
422 | ולידציית הבקשה נכשלה. ראו שגיאות ולידציה. | לא, לא ללא שינוי. |
429 | הגבלת קצב: 500 בקשות לדקה לכל IP (לכל משתמש כששולחים token). | כן, עם backoff. |
500 | יצירת המשלוח עצמה נכשלה — דחייה של חברת השילוח מדווחת כ-500 עם הטקסט של חברת השילוח ב-message — או שגיאת שרת בלתי מטופלת, או Error Retrieving Locations מ-pickup_locations. | רק במקרים הבלתי-מטופלים / pickup_locations; דחייה של חברת השילוח תיכשל באופן זהה עד שגוף הבקשה ישתנה. |
ניסיון חוזר ל-open_new_order בטוח למשך שעה
תגובת open_new_order מוצלחת נשמרת במטמון לשעה לפי license_key + order.id, וקריאה חוזרת בתוך החלון הזה מחזירה את התגובה מהמטמון כפי שהיא, בתוספת data.idempotent: true, במקום ליצור משלוח שני. כשלים אינם נשמרים במטמון, ולכן יצירה שנכשלה נשארת ניתנת לניסיון חוזר. זו הסיבה שאפשר לנסות שוב 409 או timeout ברשת בלי לייצר כפילות: הבקשה שסיימה ראשונה היא הבעלים של קוד המעקב, והניסיון החוזר מקבל אותו.
שימו לב שההסרה הזו של כפילויות מוגבלת בזמן ואינה קבועה — יצירת משלוח שני לאותה הזמנה כן מותרת לאחר שחלפה השעה, מפני שסוחרים באופן לגיטימי שולחים את שאר ההזמנה במועד מאוחר יותר.
שגיאות ולידציה
איזה גוף נושא 422 תלוי באיזו נקודת קצה קראתם, מפני ש-v1 מעולם לא אחדה את זה.
| נקודת קצה | מבנה | Status |
|---|---|---|
open_new_order | המעטפת של v1, עם שגיאות השדות תחת data.errors וההודעה הראשונה מועתקת אל message. | 422 |
get_order_details, get_shipping_status, change_order_status, label, order-label, bulk-label, same_day_delivery_quote, והמקרה של license_key/domain חסרים ב-middleware של הרישיון | גוף הוולידציה המוגדר כברירת מחדל ב-Laravel: {"message": ..., "errors": {...}}. בלי מפתח success. | 422 |
order (חיפוש משלוחים מרוכז) | מבנה שלישי: {"errors": {...}, "message": "Validation Failed", "success": false} — מוחזר עם HTTP 500, לא 422. | 500 |
open_new_order, נקודת הקצה שרוב האינטגרציות מתעניינות בה, נראית כך:
{
"data": {
"errors": {
"ship_data.city": ["City is required"],
"order_items.0.quantity": ["Each order item must have a quantity"]
}
},
"message": "City is required",
"success": false,
"warning": ""
}הכללים המבניים שלה הם ש-ship_data ו-order חייבים שניהם להיות מערכים, ושכל רשומה ב-order_items צריכה name, quantity שלם של 1 לפחות, ו-total מספרי. מעל אלה היא מריצה בדיקות חוצות-שדות שמקבלות את הערך מכל אחד מהמקומות: רחוב מ-ship_data.street או מ-order.billing.address_1, עיר מ-ship_data.city או מ-order.billing.city, ושם איש קשר מ-ship_data.contact_name או מ-order.billing.first_name/last_name. רק ערך ריק באמת נכשל — "0" הוא ערך רחוב לגיטימי והוא מתקבל. גם ship_data: {} ריק נכשל, מפני ש-required של Laravel מתייחס למערך ריק כאל חסר.
גוף שגיאה אחד יכול לערב שתי שפות
נבדק מול מכונה חיה: ההודעות של ה-framework מוחזרות בלוקאל של השרת (עברית במכונות הישראליות), בעוד שהכללים שמוגדרים בנקודת הקצה הזו נושאים הודעות באנגלית. תשובה אחת יכולה להכיל את שתיהן:
{
"data": {
"errors": {
"ship_data": ["שדה ship data הוא חובה."],
"ship_data.city": ["City is required"]
}
},
"message": "שדה ship data הוא חובה.",
"success": false,
"warning": ""
}message הוא תמיד רק העתק של השגיאה הראשונה, ולכן הוא יורש את השפה שבה היא נכתבה. אל תסתמכו על טקסט ההודעה. התאימו לפי סטטוס ה-HTTP ולפי מפתחות השדות ב-data.errors — שניהם יציבים ואינם תלויי לוקאל.
שלחו תמיד Accept: application/json
רק open_new_order כופה גוף JSON בכשל ולידציה. שאר נקודות הקצה מסתמכות על ה-content negotiation של Laravel, ולכן בקשה בלי Accept: application/json מקבלת הפניית HTTP במקום גוף 422. שלחו את הכותרת בכל קריאה.
שני מבנים שאינם המעטפת
כדאי לטפל בהם כמקרים מיוחדים בלקוח, מפני ששניהם אינם נושאים success:
{"message": ...}חשוף ב-404— חיפושי ה-firstOrFailהמפורטים בטבלת הסטטוסים.{"error": "Invalid response from shipping company"}חשוף ב-HTTP200—labelו-bulk-labelכאשר לא ניתן היה להפיק כתובת של תווית. לקוח שקורא אתdata.urlבלי לבדוק קודם אם קייםerrorיראה כתובת undefined ולא שגיאה.
לקוח v1 הגנתי מתייחס אפוא לתגובה כמוצלחת רק כאשר סטטוס ה-HTTP הוא 2xx וגם success === true וגם לא קיים מפתח error ברמה העליונה.
איפה לחפש כשקריאה נכשלת
כל בקשה שעוברת בקבוצת השילוח נכתבת לטבלה api_request_logs לפני שהיא נשלחת — URL, כותרות, גוף, מספר הזמנה וקוד מעקב — והתגובה נכתבת בחזרה לאותה שורה, כולל הגוף המנורמל המדויק שהקורא קיבל. קריאות לצד חברת השילוח נרשמות שם גם כן. כשסוחר מדווח על משלוח שלא הופיע, השורה הזו היא התיעוד של מה שנשלח ומה שחזר; ציינו את קוד המעקב או את מספר ההזמנה כשאתם פונים לתמיכה של ShipOS.