DEV Community

Cover image for A Step-by-Step Guide to Implement Webhook Workflows in Flight Booking Systems
Jacky
Jacky

Posted on • Edited on

A Step-by-Step Guide to Implement Webhook Workflows in Flight Booking Systems

As the Solution Architect behind the initial design of a Flight Ticket Booking System for a startup, my journey took an exciting turn as we ventured into the realm of enabling Travel Agents to participate in our system seamlessly. In this article, I'll share my insights and experiences in architecting a webhook solution, allowing Travel Agents to receive real-time notifications about newly displayed flights.

Understanding the Need for Travel Agent Integration

As our flight ticket booking system gained traction, the demand for collaboration with Travel Agents became evident. Travel Agents, with their established clientele, sought a direct and efficient way to stay informed about the latest flight offerings. This prompted the exploration of a webhook solution, aiming to bridge the gap between our system and external travel partners.

Read more: Designing Database for a Flight Ticket Booking System in a Startup: A Tale of MVP and Scalability

Extending the System Architecture for Webhooks

Our initial system was built on a solid foundation of Java, Spring Boot, PostgreSQL, and a Monolithic architecture. The challenge now was to seamlessly integrate webhooks into this existing structure.

Benefits and Future Prospects

The integration of webhooks into our Flight Ticket Booking System brings forth several benefits:

  • Real-Time Collaboration: Travel Agents receive instant notifications about newly displayed flights, enabling swift response and booking for their clients.
  • Efficiency and Accuracy: The webhook solution ensures that Travel Agents have accurate and up-to-date information, enhancing the efficiency of their booking processes.
  • Scalability: The modular design of our system, with webhooks seamlessly integrated, positions us well for future scalability. As the system evolves, additional events and webhook functionalities can be introduced without major disruptions.

Key Components of the B2B Flight Ticket Booking System

  1. Webhook Mechanism: The core of our system lies in its Webhook mechanism, enabling real-time communication between our platform and registered Travel Agencies. Webhooks serve as a bridge, instantly notifying Travel Agents of changes in flight availability, schedules, and promotions.

  2. Database Integration: Utilizing a PostgreSQL database, we store essential information such as flight details, Travel Agency data, and webhook registrations. This ensures a centralized and organized repository, facilitating quick access to critical data for seamless operations.

  3. Flight and Webhook Entities: Our system employs two primary entities: Flight and Webhook. The Flight entity represents flight details, while the Webhook entity stores information about registered webhooks, including the webhook URL.

  4. Spring Boot Framework: Built on the Spring Boot framework, our application leverages the power of Java to provide a scalable, modular, and easily maintainable solution. Spring Boot's simplicity and convention-over-configuration approach expedite development, allowing us to focus on the core functionalities.

Webhook Workflow Design

Workflow

  1. Travel Agency Registration:

    • The Travel Agency registers a webhook with your system.
  2. Database Storage:

    • Your system stores the webhook information in the database.
  3. Subscription:

    • The Travel Agency subscribes to receive updates for flight information.
  4. Flight Information Update:

    • New flight information is added or updated in the system.
  5. Webhook Execution:

    • Your system triggers webhook execution for each registered webhook.
      1. For each webhook:
      2. Execute webhook request to the Travel Agency's specified URL.
      3. If successful, mark the webhook execution as completed.
      4. If failed, retry the webhook execution based on configured retry policies.
  6. End of Workflow.

Step by step to implement

Step 1: Set Up the Spring Boot Project

You can use Spring Initializer (https://start.spring.io/) to generate a Spring Boot project with the required dependencies. For this example, include "Spring Web" and "Spring Data JPA."

Step 2: Implement Webhook Entity

Create a Webhook entity to store information about registered webhooks.

// Webhook.java
@Entity
@Table(name = "webhooks")
public class Webhook {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String url;

        private Strign travelAgentId;
    // getters and setters
}
Enter fullscreen mode Exit fullscreen mode

Implement Webhook Service

Create a service to manage webhooks. This service will handle registering webhooks, triggering updates, and managing tasks related to webhooks.

// WebhookService.java
@Service
public class WebhookService {

    @Autowired
    private WebhookRepository webhookRepository;

    // Implement methods for webhook registration, update, and task management
    // Example methods: registerWebhook, updateWebhook, triggerUpdate
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Implement Webhook Controller

Create a controller to handle webhook-related endpoints.

// WebhookController.java
@RestController
@RequestMapping("/webhooks")
public class WebhookController {

    @Autowired
    private WebhookService webhookService;

    @PostMapping
    public ResponseEntity<String> registerWebhook(@RequestBody String webhookUrl) {
        // Implement webhook registration logic
        // Example: webhookService.registerWebhook(webhookUrl);
        return ResponseEntity.ok("Webhook registered successfully");
    }

    // Add other endpoints for webhook management
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Implement Flight Service

Create a service to manage flights. This service will include methods to get a list of flights and update flights.

// FlightService.java
@Service
public class FlightService {

    @Autowired
    private FlightRepository flightRepository;

    // Implement methods for getting flights and updating flights
    // Example methods: getAllFlights, updateFlight
}
Enter fullscreen mode Exit fullscreen mode

Implement Flight Controller

Create a controller to handle flight-related endpoints.

// FlightController.java
@RestController
@RequestMapping("/flights")
public class FlightController {

    @Autowired
    private FlightService flightService;

    @GetMapping
    public ResponseEntity<List<Flight>> getAllFlights() {
        // Implement logic to get a list of flights
        // Example: List<Flight> flights = flightService.getAllFlights();
        return ResponseEntity.ok(flights);
    }

    // Add other endpoints for flight management
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Implement Webhook Execution Service

Create a service to handle the execution of webhooks.

// WebhookExecutionService.java
@Service
public class WebhookExecutionService {

    @Autowired
    private RestTemplate restTemplate;

    public void executeWebhook(Webhook webhook, String payload) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<String> request = new HttpEntity<>(payload, headers);

        restTemplate.postForObject(webhook.getUrl(), request, String.class);
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 6: Update Webhook Service

Update the WebhookService to include methods for triggering webhook updates.

// WebhookService.java
@Service
public class WebhookService {

    @Autowired
    private WebhookRepository webhookRepository;

    @Autowired
    private WebhookExecutionService webhookExecutionService;

    public void registerWebhook(String url) {
        // Implement registration logic
    }

    public void updateWebhook(Long webhookId, String newUrl) {
        // Implement update logic
    }

    public void triggerUpdate(Long webhookId, String payload) {
        Webhook webhook = webhookRepository.findById(webhookId)
                .orElseThrow(() -> new EntityNotFoundException("Webhook not found"));

        webhookExecutionService.executeWebhook(webhook, payload);
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 7: Update Flight Service

Update the FlightService to trigger webhooks when flights are added or updated.

// FlightService.java
@Service
public class FlightService {

    @Autowired
    private FlightRepository flightRepository;

    @Autowired
    private WebhookService webhookService;

    public List<Flight> getAllFlights() {
        // Implement logic to get a list of flights
        return flightRepository.findAll();
    }

    public void addOrUpdateFlight(Flight flight) {
        // Implement logic to add or update a flight
        flightRepository.save(flight);

        // Trigger webhooks with the updated flight information
        List<Webhook> webhooks = webhookService.getAllWebhooks();
        String payload = "New flight information: " + flight.toString();

        for (Webhook webhook : webhooks) {
            webhookService.triggerUpdate(webhook.getId(), payload);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 8: Update Flight Controller

Update the FlightController to include an endpoint for adding or updating flights.

// FlightController.java
@RestController
@RequestMapping("/flights")
public class FlightController {

    @Autowired
    private FlightService flightService;

    @PostMapping
    public ResponseEntity<String> addOrUpdateFlight(@RequestBody Flight flight) {
        flightService.addOrUpdateFlight(flight);
        return ResponseEntity.ok("Flight added or updated successfully");
    }

    // Add other endpoints for flight management
}
Enter fullscreen mode Exit fullscreen mode

Step 9: Configure RestTemplate

Configure RestTemplate in your application configuration to handle HTTP requests.

// ApplicationConfiguration.java
@Configuration
public class ApplicationConfiguration {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
Enter fullscreen mode Exit fullscreen mode

Run the Application

Run your Spring Boot application again. Now, when you add or update flights, registered webhooks will be triggered, notifying the subscribed Travel Agencies about the changes.

Keep in mind that this is a simplified example, and in a production environment, you would need to handle errors, secure your webhooks, and implement other best practices for reliability and security. Additionally, consider adding asynchronous processing for webhook execution to avoid blocking the main application thread.

Handler Error With Retry

To implement retry strategies for webhook execution in the event of failures, you can leverage the Spring Retry library. This library provides annotations and utilities for retrying failed operations. Let's modify the existing code to include retry functionality.

Step 10: Add Spring Retry Dependency

Update your pom.xml or build.gradle file to include the Spring Retry dependency:

For Maven:

<dependencies>
    <!-- Other dependencies -->
    <dependency>
        <groupId>org.springframework.retry</groupId>
        <artifactId>spring-retry</artifactId>
    </dependency>
</dependencies>
Enter fullscreen mode Exit fullscreen mode

For Gradle:

dependencies {
    // Other dependencies
    implementation 'org.springframework.retry:spring-retry'
}
Enter fullscreen mode Exit fullscreen mode

Step 11: Update Webhook Execution Service

Modify the WebhookExecutionService to include retry logic using the @Retryable annotation.

// WebhookExecutionService.java
@Service
public class WebhookExecutionService {

    @Retryable(
        value = { HttpServerErrorException.class },
        maxAttempts = 3,
        backoff = @Backoff(delay = 1000)
    )
    public void executeWebhook(Webhook webhook, String payload) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        HttpEntity<String> request = new HttpEntity<>(payload, headers);

        restTemplate.postForObject(webhook.getUrl(), request, String.class);
    }
}
Enter fullscreen mode Exit fullscreen mode

In this example, the @Retryable annotation is used to specify that the method should be retried in case of HttpServerErrorException, with a maximum of 3 attempts and a backoff delay of 1000 milliseconds between attempts.

Step 12: Enable Retry in Application

Enable retry in your Spring Boot application by adding the @EnableRetry annotation to your main application class.

// YourApplication.java
@SpringBootApplication
@EnableRetry
public class YourApplication {

    public static void main(String[] args) {
        SpringApplication.run(YourApplication.class, args);
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

The journey from designing the initial Flight Ticket Booking System to incorporating webhooks for Travel Agent integration has been a testament to adaptability and foresight. By extending our system architecture to embrace webhooks, we have empowered Travel Agents to actively participate in our ecosystem, fostering a collaborative and efficient travel booking experience. As we continue to refine and enhance our system, the role of webhooks will undoubtedly be pivotal in shaping the future of our Flight Ticket Booking System. Stay tuned for more updates on our ongoing journey in the world of travel technology.

Top comments (4)

Collapse
 
aniket_katakdhond_ffc807e profile image
Aniket Katakdhond

Same question as @ak_mkoder_110f8a927edb1bb . When we add new flights, flights will be added in db, and webhook service will initiate to send api call to webhook.url. I suppose this webhook.url is some frontend url right? I am not able to understand like how will the frontend listen when we post from webhook service with rest template. Cause afaik, frontends don't directly deal with post requests.

Collapse
 
jackynote profile image
Jacky

I think we got misunderstanding in this article, I mentioned and designed for a B2B system, it means that the system's agency would be integrated with our saystem through backend to backend without Front-end. In agency side, firstly, they should define and expose an API to get data when webhook call, and then they need use this API that they just defined to call register webook URL and integrate into our system.

Collapse
 
ak_mkoder_110f8a927edb1bb profile image
AK MKoder

How does the client retrieve the Payload from webhook POST call?

Collapse
 
jackynote profile image
Jacky

From Travel Agency, we need define a Webhook Listener Endpoint as an example:

@RestController
@RequestMapping("/agency/webhooks")
public class WebhookListenerController {

    private static final Logger logger = LoggerFactory.getLogger(WebhookListenerController.class);

    @PostMapping("/flight-updates")
    public ResponseEntity<String> receiveFlightUpdate(@RequestBody String flightData) {
        // Log or process the flight data
        logger.info("Received flight data: {}", flightData);

        // You can add custom logic to process the flight data here
        // Example: storing the data in a database, notifying users, etc.

        return ResponseEntity.ok("Flight update received successfully");
    }
}
Enter fullscreen mode Exit fullscreen mode

And then you can integrate and register webhook url with Flight backend system with this api, eg: http://localhost:8080/agency/webhooks/flight-updates.

When Flights are updated status, the system will call a webhook http://localhost:8080/agency/webhooks/flight-updates to notify with payload that we defined.