Software
Türkçe okuFrom Loyalty Credentials to Flight Offers: Identity-Based Airline Retailing
We’ll walk through the Loyalty Credential, digital wallet, personalized offer selection, and Order Credential workflows in the DemoAir app step by step, using code examples.
Digital Travel Architecture — Part 3
In the early stages of the Digital Travel app, the traveler’s passport information had been verified, a Passport Credential had been created, and it had been added to the digital wallet. In this section, we focus on how the same wallet can be used during the travel booking process: The traveler’s loyalty program membership is verified, flight offers are generated based on their membership tier, and the selected offer is saved back to the wallet as an Order Credential.
The FastAPI and React workflow in the DemoAir app developed throughout this article is used as a reference. The goal is not to evaluate the source code, but to explain the airline retailing journey modeled by the app through its technical components.
1. Transition from the Identity Layer to the Retailing Layer
In the application, the digital wallet is not merely a space that stores a Passport Credential. Multiple verifiable records that the passenger can use at different stages of their journey are consolidated under the same wallet:
- Passport Credential: The passenger’s identity and travel document information
- Loyalty Credential: Loyalty program, membership number, tier, and points information
- Order Credential: Information on the selected flight and the created order
Thanks to this structure, the shopping process can be tailored not only based on the route and date but also according to the passenger’s valid membership information in their wallet.
Passport Credential
│
▼
Digital Wallet ── Loyalty Credential
│
▼
Offer Search
│
▼
Kişiselleştirilmiş uçuş teklifleri
│
▼
Seçilen Offer
│
▼
Order Credential
│
▼
Digital Wallet
2. Creating the Loyalty Credential
When the third step in the React interface is initiated, the frontend first /retail/loyalty/enroll its endpoint. The request contains the user’s ID along with basic information about the loyalty program:
{
"user_id": "traveller-123",
"program_name": "DemoAir Miles",
"member_id": "MEMBER-123456",
"tier": "GOLD",
"points": 42000
}
The backend converts these fields into Loyalty Credential claims. The shared credential service generates a JWT containing the issuer, subject, creation time, validity period, credential type, and claim fields.
claims = {
"program_name": request.program_name,
"member_id": request.member_id,
"tier": request.tier.upper(),
"points": request.points,
}
loyalty_vc = build_loyalty_vc(
subject_id=request.user_id,
claims=claims,
)
After the created credential is validated, loyalty it is added to the user’s digital wallet based on its type. This allows the offer service to read membership information directly from the credential in the wallet, rather than retrieving it again from the frontend form.
3. Offer Search Request
After the Loyalty Credential is created, the frontend initiates the offer search process for the same user. The shopping request contains four key fields:
searchOffers(
userId,
origin,
destination,
departureDate
)
The request model on the backend also represents this travel context:
class OfferSearchRequest(BaseModel):
user_id: str
origin: str
destination: str
departure_date: str
At this stage, the retail service combines two different data sources. The route and date come from the travel search on the frontend, while the loyalty level comes from the credential in the digital wallet.
4. Retrieving the Loyalty Level from the Wallet
The offer service first searches through the user’s wallet records loyalty . Once the credential is validated, the tier :
stored_loyalty = wallet_repo.get_first_by_type(
request.user_id,
"loyalty",
)
if stored_loyalty:
payload = verify_vc(stored_loyalty.vc_jwt)
loyalty_claims = payload.get("claims", {})
tier = loyalty_claims.get("tier")
In the DemoAir example, the membership level determines the discount rate applied to the offer price:
- PLATINUM: 20%
- GOLD: 15%
- SILVER: 10%
- BASIC or no membership: standard price
def discount_for_tier(tier: str) -> float:
if tier == "PLATINUM":
return 0.20
if tier == "GOLD":
return 0.15
if tier == "SILVER":
return 0.10
return 0.00
Thus, loyalty information becomes a verifiable retail input that influences the user experience.
5. Generating Flight Offers
The demo offer engine generates three different flight options. For each offer, a unique offer_idflight number, route, date, base price, loyalty discount, final price, and currency are generated.
offer = OrderOffer(
offer_id=str(uuid.uuid4()),
flight_no=flight_number,
origin=request.origin,
destination=request.destination,
departure_date=request.departure_date,
base_price=base_price,
loyalty_discount=round(base_price * discount_rate, 2),
final_price=round(base_price - loyalty_discount, 2),
currency="EUR",
applied_tier=tier,
)
The offer response displays not only the final price but also how the price was calculated on the interface. The user can see the base price, the applied discount, the loyalty level, and the total amount to be paid all at once.
6. Offer Selection in the React Interface
The offers returned from the backend are stored in React state and listed as cards. Each card displays the following information:
- Flight number
- Departure and arrival locations
- Flight date
- Base price
- Loyalty discount
- Applicable membership level
- Final price and currency
When a passenger clicks on a card, the selected offer is added to the application state. The flight number and price are transferred to the Order screen in the next step of the journey:
function selectOffer(offer: Offer) {
setSelectedOffer(offer)
setSelectedFlightNo(offer.flight_no)
setFinalPrice(offer.final_price)
}
This transition represents the user confirmation point between the shopping phase and the order creation phase.
7. Creating an Order from the Selected Offer
When the user confirms their selection, the frontend /retail/orders/create endpoint. In the demo application, the Order request carries the route, date, price, and currency information for the selected flight.
{
"user_id": "traveller-123",
"flight_no": "DA101",
"origin": "IST",
"destination": "LHR",
"departure_date": "2026-12-30",
"final_price": 127.50,
"currency": "EUR"
}
The Retail service generates a new order_id and converts the flight information into Order Credential claims:
order_claims = {
"order_id": str(uuid.uuid4()),
"flight_no": request.flight_no,
"origin": request.origin,
"destination": request.destination,
"departure_date": request.departure_date,
"price": request.final_price,
"currency": request.currency,
}
order_vc = build_order_vc(
subject_id=request.user_id,
claims=order_claims,
)
Like the Passport and Loyalty credentials, the Order Credential is also signed in JWT format and added to the user’s wallet.
8. The Wallet’s Evolution Throughout the Journey
Once the Order transaction is complete, the user’s digital wallet now contains data from three different business domains:
Wallet / traveller-123
├── Passport Credential
│ └── nationality, document data, validity
├── Loyalty Credential
│ └── program, member ID, tier, points
└── Order Credential
└── order ID, flight, route, date, price
This combination forms the necessary input for the next step: airline check-in. The check-in service retrieves the user’s nationality from the Passport Credential and the route and flight number from the Order Credential to initiate the Travel Rules assessment.
9. The IATA Offers & Orders Context
IATA’s Modern Airline Retailing approach aims to create airline products as “Offers” based on the customer and shopping context, and to manage accepted offers as “Orders” throughout their lifecycle.
NDC provides a data exchange approach for sharing airline offer and order information in a standardized format across different sales channels. ONE Order, on the other hand, develops the concept of a single, integrated customer order to replace separate records such as reservations, e-tickets, and EMDs.
The DemoAir application makes the core workflow visible on a small prototype rather than implementing the message structures of these standards:
Shop → Offer oluştur → Offer seç → Order oluştur
↑
Loyalty Credential bağlamı
10. End-to-End Example Scenario
- The passenger enters the IST–LHR route and travel dates.
- A Loyalty Credential is created for the DemoAir Miles membership.
- The credential is added to the user’s digital wallet.
- The Offer service reads the GOLD level in the wallet.
- Three flight options are generated with the loyalty discount applied.
- The React interface lists the offers along with their price components.
- The passenger selects flight DA101.
- An Order Credential is created for the selected flight.
- The Order Credential is saved to the wallet.
- The app proceeds to the check-in and ready-to-fly stages.
Conclusion
In this section, we saw how the Loyalty Credential in the digital wallet was integrated into the airline shopping process. The tier information within the credential was used in offer calculations, personalized flight options were presented in the React interface, and the passenger’s selection was transferred back to the wallet as an Order Credential.
Thus, the Digital Travel app evolves from a simple identity verification system into an end-to-end demo that integrates identity, loyalty program, offer, and order stages within the same passenger journey.
In the next part of the series, we’ll examine how the Passport Credential and Order Credential are integrated with the Travel Rules service and ready_to_fly how the result is generated during the check-in process.
Demo and Security Note
This article was created using a working demo codebase prepared for development and proof-of-concept purposes. The identity, credential, wallet, offer, and order processes in the application have been simplified compared to real airline systems.
The sample code should be considered a low-security prototype; it must not be used directly with real passenger data, payment transactions, or in live airline operations. The application does not claim full compliance with IATA NDC, ONE Order, or any other industry standard.
Official Sources
How would you rate this article?
Your feedback helps improve future articles.