Google Wallet Integration for Boarding Passes in Mobile Apps
A boarding pass in Google Wallet is a FlightObject on top of FlightClass. The key difference from EventTicket: Google Wallet automatically updates flight status (delays, gate changes) through integration with aviation databases — if you correctly populate the flightHeader fields.
FlightClass and FlightObject: Structure
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", # IATA airline code
"airlineLogo": {
"sourceUri": {"uri": "https://yourapp.com/logo.png"}
},
"airlineName": {
"defaultValue": {"language": "en", "value": "Aeroflot"}
}
},
"flightNumber": flight_number,
"operatingCarrier": {
"carrierIataCode": "SU"
}
},
"origin": {
"airportIataCode": origin, # "SVO"
"terminal": "D",
"gate": "D12"
},
"destination": {
"airportIataCode": destination # "LED"
},
"localScheduledDepartureDateTime": departure_time, # "2024-06-15T14:30:00"
"reviewStatus": "UNDER_REVIEW" # or APPROVED after verification
}
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"}
}
}
The barcode.value field for airline tickets is a BCBP (Bar Coded Boarding Pass) string per IATA standard. Airport scanners expect exactly this format, not an arbitrary ID.
JWT and App Transfer
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")
Android: Saving Boarding Pass
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 {
// Pass already added — update via API
showAlreadySaved()
}
}
}
If intentSender is null, a pass with that objectId already exists in Wallet — Google won't offer to add a duplicate. This is normal behavior.
Automatic Updates on Flight Changes
This is the main feature of FlightObject compared to GenericObject. Google monitors flight status by carrierIataCode + flightNumber + localScheduledDepartureDateTime. On delay or gate change, the user receives a notification from Google Wallet automatically — without additional logic on your side.
For this, the class reviewStatus must be APPROVED, and flight data must match aviation databases. Status UNDER_REVIEW means the class isn't verified yet by Google; automatic updates won't work.
Issuer Account Verification
Before production, the aviation issuer must pass verification at Google Pay & Wallet Console. For testing, a test account added to Test Devices is sufficient.
Timeline
2–3 days. Server-side JWT generation with BCBP barcode, button integration, testing — 2 days. Setting up auto-updates for flight status — additionally 0.5–1 day. Pricing is calculated individually.







