FlightObject ו-FlightClass לכרטיסי עלייה למטוס
כשמנסים להוסיף כרטיס עלייה למטוס ל-Google Wallet, מפתחים נתקלים לעיתים קרובות בבעיה: הברקוד לא נקרא על ידי סורקי שדה התעופה, ועדכוני מצב טיסה אוטומטיים לא מגיעים. הסיבה היא BCBP לא תקני או קוד IATA שגוי ב-FlightClass. ב-80% מהמקרים, carrierIataCode שגוי מונע מהמשתמשים לקבל התראות על עיכובים. יש לנו 5 שנות ניסיון ו-20+ פרויקטים של שילוב Google Wallet לחברות תעופה. תקן ה-IATA BCBP מגדיר את מבנה הנתונים לסריקה. קידוד שגוי מוביל לדחיות בשער העלייה למטוס. החיסכון מאוטומציה של תהליך העלייה למטוס דרך Wallet יכול להגיע ל-$9k–13k בשנה עבור חברת תעופה עם מיליון נוסעים.
בעיות בשילוב Google Wallet
הקושי העיקרי הוא המבנה הנכון של FlightClass ו-FlightObject. שגיאה ב-flightHeader (לדוגמה, קוד IATA שגוי של חברת תעופה) משביתה התראות אוטומטיות. הבעיה השנייה היא יצירת JWT עם BCBP נכון. מפתחים רבים משתמשים במזהה שרירותי, אבל הסורקים מצפים לתקן IATA. השלישית היא אימות מנפיק: בלעדיו, המחלקה נשארת במצב UNDER_REVIEW, ועדכונים אוטומטיים לא עובדים. אנחנו פותרים את כל שלוש המשימות, ומבטיחים פעולה תקינה בסביבת ייצור.
שגיאות נפוצות והפתרונות שלהן
| שגיאה | השלכות | פתרון |
|---|---|---|
| קוד IATA שגוי של חברת תעופה | אין עדכונים אוטומטיים | בדוק את הקוד במדריך IATA |
| פורמט BCBP שגוי | הברקוד לא נקרא | השתמש בתקן IATA |
| חסר אימות מנפיק | המחלקה תקועה ב-UNDER_REVIEW | השלם אימות בקונסולה |
מבנה FlightClass ו-FlightObject
FlightClass מתאר את הטיסה — חברת תעופה, מספר, מסלול. FlightObject מייצג נוסע ספציפי ומושב. ההבדל המרכזי: FlightObject תומך בעדכוני מצב טיסה אוטומטיים מ-Google אם המנפיק מאומת. להלן דוגמאות Python ליצירה.
def create_flight_class(flight_number: str, origin: str, destination: str, departure_time: str) -> dict:
issuer_id = "YOUR_ISSUER_ID"
class_id = f"{issuer_id}.flight_{flight_number}"
return {
"id": class_id,
"issuerName": "AirCompany",
"flightHeader": {
"carrier": {
"carrierIataCode": "SU",
"airlineLogo": {
"sourceUri": {"uri": "https://yourapp.com/logo.png"}
},
"airlineName": {
"defaultValue": {"language": "ru", "value": "Аэрофлот"}
}
},
"flightNumber": flight_number,
"operatingCarrier": {
"carrierIataCode": "SU"
}
},
"origin": {
"airportIataCode": origin,
"terminal": "D",
"gate": "D12"
},
"destination": {
"airportIataCode": destination
},
"localScheduledDepartureDateTime": departure_time,
"reviewStatus": "UNDER_REVIEW"
}
def create_flight_object(class_id: str, passenger_name: str, seat: str, booking_ref: str) -> dict:
object_id = f"YOUR_ISSUER_ID.bp_{booking_ref}"
return {
"id": object_id,
"classId": class_id,
"state": "ACTIVE",
"passengerName": passenger_name,
"boardingAndSeatingInfo": {
"seatNumber": seat,
"boardingGroup": "A",
"seatClass": "ECONOMY"
},
"reservationInfo": {
"confirmationCode": booking_ref,
"eticketNumber": f"555-{booking_ref}"
},
"barcode": {
"type": "QR_CODE",
"value": f"M1{passenger_name.upper().replace(' ', '')[:20]}{booking_ref}SVO LED SU 123 1",
"alternateText": booking_ref
},
"securityProgramLogo": {
"sourceUri": {"uri": "https://yourapp.com/security-badge.png"}
}
}השדה def create_flight_class(flight_number: str, origin: str, destination: str, departure_time: str) -> dict: issuer_id = "YOUR_ISSUER_ID" class_id = f"{issuer_id}.flight_{flight_number}" return { "id": class_id, "issuerName": "AirCompany", "flightHeader": { "carrier": { "carrierIataCode": "SU", "airlineLogo": { "sourceUri": {"uri": "https://yourapp.com/logo.png"} }, "airlineName": { "defaultValue": {"language": "ru", "value": "Аэрофлот"} } }, "flightNumber": flight_number, "operatingCarrier": { "carrierIataCode": "SU" } }, "origin": { "airportIataCode": origin, "terminal": "D", "gate": "D12" }, "destination": { "airportIataCode": destination }, "localScheduledDepartureDateTime": departure_time, "reviewStatus": "UNDER_REVIEW" } def create_flight_object(class_id: str, passenger_name: str, seat: str, booking_ref: str) -> dict: object_id = f"YOUR_ISSUER_ID.bp_{booking_ref}" return { "id": object_id, "classId": class_id, "state": "ACTIVE", "passengerName": passenger_name, "boardingAndSeatingInfo": { "seatNumber": seat, "boardingGroup": "A", "seatClass": "ECONOMY" }, "reservationInfo": { "confirmationCode": booking_ref, "eticketNumber": f"555-{booking_ref}" }, "barcode": { "type": "QR_CODE", "value": f"M1{passenger_name.upper().replace(' ', '')[:20]}{booking_ref}SVO LED SU 123 1", "alternateText": booking_ref }, "securityProgramLogo": { "sourceUri": {"uri": "https://yourapp.com/security-badge.png"} } } הוא מחרוזת BCBP לפי תקן IATA, המכילה מעל 20 שדות. סורקי שדה התעופה מצפים לפורמט המדויק הזה. ה-BCBP נוצר בשרת בעת יצירת ה-FlightObject. כל השדות חייבים להיות מלאים כראוי: קוד חברת תעופה, מספר טיסה, מסלול, תאריך, שם נוסע, מושב ומספר הזמנה. שגיאה בכל שדה גורמת לדחייה על ידי הסורק. אנו משתמשים בספרייה מוכחת שבונה את המחרוזת לפי מפרט IATA.
יצירת JWT ושמירה באנדרואיד
ה-JWT נחתם עם מפתח שרת ומועבר לאפליקציה. דוגמת יצירה ב-Python:
def generate_boarding_pass_jwt(flight_object: dict) -> str:
payload = {
"iss": service_account_email,
"aud": "google",
"typ": "savetowallet",
"iat": int(time.time()),
"payload": {
"flightObjects": [flight_object]
}
}
return jwt.encode(payload, private_key, algorithm="RS256")באנדרואיד, השמירה מופעלת דרך barcode.value:
fun saveBoardingPass(jwt: String) {
val request = SavePassesRequest.newBuilder().setJwt(jwt).build()
walletClient.savePassesViaIntent(request) { result ->
result.intentSender?.let { sender ->
addToWalletLauncher.launch(
IntentSenderRequest.Builder(sender).build()
)
} ?: run {
showAlreadySaved()
}
}
}אם def generate_boarding_pass_jwt(flight_object: dict) -> str: payload = { "iss": service_account_email, "aud": "google", "typ": "savetowallet", "iat": int(time.time()), "payload": { "flightObjects": [flight_object] } } return jwt.encode(payload, private_key, algorithm="RS256") הוא null, כבר קיים כרטיס עם SavePassesRequest זה — Google לא יציע כפילות. זו התנהגות תקינה.
למה FlightObject עדיף על GenericObject?
FlightObject מקבל אוטומטית עדכוני טיסה מ-Google. GenericObject הוא רק מיכל נתונים. השוואה:
| תכונה | FlightObject | GenericObject |
|---|---|---|
| עדכוני מצב טיסה אוטומטיים | כן | לא |
| תמיכה בברקוד BCBP | כן (IATA) | לא |
| התראות עיכוב | אוטומטיות | סנכרון ידני |
| אימות מנפיק | נדרש | לא נדרש |
FlightObject עם עדכונים אוטומטיים מגביר את מעורבות הנוסעים פי 3 בהשוואה לכרטיס PDF. הלקוחות שלנו מדווחים על הפחתה של 40% בעומס התמיכה, השווה לחיסכון של $4.5k–6.5k בשנה.
איך להבטיח עדכוני טיסה אוטומטיים?
כדי להשיג זאת:
- הגדר את
fun saveBoardingPass(jwt: String) { val request = SavePassesRequest.newBuilder().setJwt(jwt).build() walletClient.savePassesViaIntent(request) { result -> result.intentSender?.let { sender -> addToWalletLauncher.launch( IntentSenderRequest.Builder(sender).build() ) } ?: run { showAlreadySaved() } } }של המחלקה ל-intentSenderלאחר אימות המנפיק. - נתוני הטיסה (
objectId,reviewStatus,APPROVED) חייבים להתאים בדיוק למאגרי הנתונים של חברת התעופה. - Google עוקב אחר מצב הטיסה באמצעות שדות אלה ושולח התראות ללא לוגיקה נוספת.
אימות מנפיק הוא שלב מפתח. זה בחינם אבל דורש מסמכים נכונים. אנחנו עוזרים להכין אותם ומשלימים את התהליך תוך 3-5 ימים. החיסכון מהתראות עיכוב אוטומטיות מגיע עד $2.7k–3.9k בשנה על ידי הפחתת עומס מוקד הטלפוני.
תהליך ולוח זמנים
- ניתוח — אנו בוחנים את מבנה האפליקציה שלך ומזהים נקודות שילוב.
- עיצוב — אנו יוצרים את הסכימה של FlightClass ו-FlightObject, המכסה את כל השדות.
- יישום — אנו כותבים יצירת JWT בצד השרת ומוסיפים את כפתור השמירה.
- בדיקות — אנו בודקים על מכשירי בדיקה בקונסולת Google Wallet.
- פריסה — אנו מפרסמים את העדכון ומסייעים באימות המנפיק.
יצירת JWT בצד השרת עם BCBP, שילוב כפתור ובדיקות אורכים מיומיים. הגדרת עדכונים אוטומטיים אורכת עד יום נוסף. התמחור מחושב באופן אישי.
מה כלול
- תיעוד API עם דוגמאות Python ו-Kotlin.
- מחלקות FlightClass ו-FlightObject מוכנות לשימוש המותאמות למותג שלך.
- שילוב כפתור "הוסף ל-Google Wallet".
- הגדרת עדכוני מצב טיסה אוטומטיים.
- סיוע באימות מנפיק.
- בדיקות ותמיכה במהלך השחרור.
- הדרכה לצוות שלך על שימוש בקונסולת Google Wallet.
- מתן גישה לחשבון בדיקה.
צור קשר להערכת פרויקט. קבל ייעוץ על שילוב Google Wallet באפליקציה שלך. אנו מבטיחים פעולה תקינה מהשחרור הראשון.







