Airline Package Selection Integration
This tutorial will guide you through integrating Hubby eSIM package selection into your airline booking flow, focusing on maximizing conversions while maintaining a seamless user experience.
Overview
A successful airline integration should:
- Present relevant eSIM packages based on the flight itinerary
- Integrate smoothly with the existing booking flow
- Provide clear value proposition to travelers
- Handle multi-destination trips effectively
Integration Steps
1. Flight Itinerary Analysis
class ItineraryAnalyzer {
constructor(hubbyClient) {
this.hubby = hubbyClient;
}
async analyzeItinerary(flights) {
const destinations = this.extractDestinations(flights);
const coverage = await this.getPackageCoverage(
destinations
);
return {
destinations,
coverage,
recommendedPackages: this.getRecommendations(
destinations,
coverage
)
};
}
extractDestinations(flights) {
const destinations = new Set();
for (const flight of flights) {
// Add departure and arrival countries
destinations.add(flight.departureCountry);
destinations.add(flight.arrivalCountry);
// Add layover countries if any
if (flight.layovers) {
for (const layover of flight.layovers) {
destinations.add(layover.country);
}
}
}
return Array.from(destinations);
}
async getPackageCoverage(countries) {
try {
const coverage = await this.hubby.packages.getCoverage({
countries
});
return coverage;
} catch (error) {
console.error('Failed to get coverage:', error);
throw new Error('Coverage check failed');
}
}
getRecommendations(destinations, coverage) {
// Sort packages by coverage and value
const packages = coverage.packages.sort((a, b) => {
// Prioritize packages covering all destinations
const aCoverage = this.calculateCoverage(
a,
destinations
);
const bCoverage = this.calculateCoverage(
b,
destinations
);
if (aCoverage !== bCoverage) {
return bCoverage - aCoverage;
}
// Then sort by value (data per dollar)
return (b.dataAmount / b.price) - (a.dataAmount / a.price);
});
return packages.slice(0, 3); // Top 3 recommendations
}
calculateCoverage(package, destinations) {
const covered = destinations.filter(country =>
package.countries.includes(country)
);
return covered.length / destinations.length;
}
}2. Package Selection UI
import React, { useState, useEffect } from 'react';
import { HubbyClient } from '@hubby/client';
const PackageSelector = ({ itinerary, onSelect }) => {
const [analysis, setAnalysis] = useState(null);
const [loading, setLoading] = useState(true);
const [selected, setSelected] = useState(null);
const hubby = new HubbyClient({
apiKey: process.env.HUBBY_API_KEY
});
const analyzer = new ItineraryAnalyzer(hubby);
useEffect(() => {
async function analyzeItinerary() {
try {
const result = await analyzer.analyzeItinerary(
itinerary.flights
);
setAnalysis(result);
} catch (error) {
console.error('Analysis failed:', error);
} finally {
setLoading(false);
}
}
analyzeItinerary();
}, [itinerary]);
if (loading) {
return <LoadingSpinner />;
}
return (
<div className="package-selector">
<h3>Stay Connected During Your Trip</h3>
<CoverageMap
destinations={analysis.destinations}
coverage={analysis.coverage}
/>
<div className="package-recommendations">
{analysis.recommendedPackages.map(pkg => (
<PackageCard
key={pkg.id}
package={pkg}
destinations={analysis.destinations}
selected={selected?.id === pkg.id}
onSelect={() => {
setSelected(pkg);
onSelect(pkg);
}}
/>
))}
</div>
<div className="coverage-details">
<h4>Coverage Details</h4>
<CoverageDetails
destinations={analysis.destinations}
coverage={analysis.coverage}
/>
</div>
</div>
);
};
const PackageCard = ({
package,
destinations,
selected,
onSelect
}) => (
<div
className={`package-card ${selected ? 'selected' : ''}`}
onClick={onSelect}
>
<div className="package-header">
<h4>{package.name}</h4>
<span className="price">${package.price}</span>
</div>
<div className="coverage-indicator">
<CoverageBar
covered={package.countries.filter(c =>
destinations.includes(c)
).length}
total={destinations.length}
/>
<span>
Covers {package.countries.length} countries
</span>
</div>
<div className="package-details">
<Detail icon="data" text={`${package.dataAmount} GB`} />
<Detail
icon="clock"
text={`${package.validity} Days`}
/>
<Detail
icon="speed"
text={`Up to ${package.maxSpeed} Mbps`}
/>
</div>
<Button
variant={selected ? 'primary' : 'outline'}
fullWidth
>
{selected ? 'Selected' : 'Select Package'}
</Button>
</div>
);3. Coverage Visualization
const CoverageMap = ({ destinations, coverage }) => {
const [map, setMap] = useState(null);
useEffect(() => {
// Initialize map
const mapInstance = new google.maps.Map(
document.getElementById('coverage-map'),
{
zoom: 2,
center: { lat: 20, lng: 0 }
}
);
// Add flight paths
const flightPath = new google.maps.Polyline({
path: destinations.map(d => d.coordinates),
geodesic: true,
strokeColor: '#FF8800',
strokeOpacity: 1.0,
strokeWeight: 2
});
flightPath.setMap(mapInstance);
// Add coverage overlay
coverage.countries.forEach(country => {
const polygon = new google.maps.Polygon({
paths: country.boundaries,
strokeColor: '#00B67A',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#00B67A',
fillOpacity: 0.35
});
polygon.setMap(mapInstance);
});
setMap(mapInstance);
}, [destinations, coverage]);
return (
<div id="coverage-map" className="coverage-map" />
);
};4. Booking Integration
class BookingIntegration {
constructor(hubbyClient) {
this.hubby = hubbyClient;
}
async addToBooking(flightBooking, package) {
try {
// Create eSIM booking
const booking = await this.hubby.bookings.create({
packageId: package.id,
customer: {
firstName: flightBooking.passenger.firstName,
lastName: flightBooking.passenger.lastName,
email: flightBooking.passenger.email,
phone: flightBooking.passenger.phone
},
metadata: {
flightBookingRef: flightBooking.reference,
airline: flightBooking.airline,
source: 'airline_booking'
}
});
// Add to flight booking
await this.addEsimToFlightBooking(
flightBooking.reference,
booking
);
return booking;
} catch (error) {
console.error('Booking failed:', error);
throw new Error('Failed to add eSIM to booking');
}
}
async addEsimToFlightBooking(reference, esimBooking) {
// Implementation depends on airline booking system
await airlineApi.updateBooking(reference, {
ancillaries: {
esim: {
bookingId: esimBooking.id,
packageName: esimBooking.package.name,
price: esimBooking.package.price
}
}
});
}
}Styling
.package-selector {
padding: 2rem;
background: #fff;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.coverage-map {
height: 300px;
margin: 1.5rem 0;
border-radius: 8px;
overflow: hidden;
}
.package-recommendations {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
margin: 2rem 0;
}
.package-card {
border: 2px solid #eee;
border-radius: 8px;
padding: 1.5rem;
transition: all 0.2s ease;
}
.package-card:hover {
border-color: #FF8800;
transform: translateY(-2px);
}
.package-card.selected {
border-color: #FF8800;
background: #FFF6E5;
}
.coverage-indicator {
margin: 1rem 0;
}
.coverage-bar {
height: 8px;
background: #eee;
border-radius: 4px;
overflow: hidden;
}
.coverage-bar-fill {
height: 100%;
background: #00B67A;
transition: width 0.3s ease;
}Analytics Integration
class AirlineAnalytics {
constructor() {
this.events = [];
}
trackEvent(eventName, data = {}) {
const event = {
name: eventName,
timestamp: new Date(),
data
};
this.events.push(event);
this.sendToAnalytics(event);
}
sendToAnalytics(event) {
// Send to your analytics platform
analytics.track(event.name, {
...event.data,
source: 'airline_integration'
});
}
getAttachRate() {
const bookings = this.events.filter(
e => e.name === 'flight_booking'
).length;
const esimAttached = this.events.filter(
e => e.name === 'esim_attached'
).length;
return bookings ? (esimAttached / bookings) * 100 : 0;
}
getPopularRoutes() {
const routeStats = {};
this.events
.filter(e => e.name === 'esim_attached')
.forEach(event => {
const route = event.data.route;
routeStats[route] = (routeStats[route] || 0) + 1;
});
return Object.entries(routeStats)
.sort((a, b) => b[1] - a[1]);
}
}Best Practices
Integration Placement
- Add after flight selection
- Before seat selection
- Clear value proposition
User Experience
- Seamless flow
- Clear pricing
- Easy comparison
Coverage Display
- Visual map
- Clear indicators
- Destination highlights
Performance
- Fast loading
- Efficient analysis
- Error handling
Common Issues
Multi-Destination Trips
- Analyze full itinerary
- Show coverage gaps
- Recommend best fit
Integration Issues
- Handle API errors
- Retry logic
- Fallback options
Pricing Display
- Clear breakdown
- Currency handling
- Tax inclusion
Getting Help
Need assistance with your airline integration?
- Check our airline examples
- Review the API documentation
- Contact support@hubbyesim.com