Last updated

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:

  1. Present relevant eSIM packages based on the flight itinerary
  2. Integrate smoothly with the existing booking flow
  3. Provide clear value proposition to travelers
  4. 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

  1. Integration Placement

    • Add after flight selection
    • Before seat selection
    • Clear value proposition
  2. User Experience

    • Seamless flow
    • Clear pricing
    • Easy comparison
  3. Coverage Display

    • Visual map
    • Clear indicators
    • Destination highlights
  4. Performance

    • Fast loading
    • Efficient analysis
    • Error handling

Common Issues

  1. Multi-Destination Trips

    • Analyze full itinerary
    • Show coverage gaps
    • Recommend best fit
  2. Integration Issues

    • Handle API errors
    • Retry logic
    • Fallback options
  3. Pricing Display

    • Clear breakdown
    • Currency handling
    • Tax inclusion

Getting Help

Need assistance with your airline integration?

  1. Check our airline examples
  2. Review the API documentation
  3. Contact support@hubbyesim.com