Skip to content
All articles

Tutorials

Implementing Real-Time Price Updates with WebSockets

A technical deep-dive into building live pricing feeds for travel inventory using Laravel WebSockets.

4 min readVeetek team

Travel prices move. A charter operator adjusts seat prices as a departure fills, a hotel wholesaler changes allotment rates, an exchange rate shifts during the day. If an agent has a results page or a booking screen open, they are looking at a price that may already be out of date. Polling the server every few seconds wastes resources and still leaves gaps. Pushing updates over WebSockets is a better fit.

This tutorial walks through how we push price and availability updates to open pages in a Laravel application using broadcasting, with Laravel Reverb or a compatible WebSocket server, and Livewire or Laravel Echo on the front end.

Not every price change needs to reach the browser. Broadcasting every update for every product to every user creates noise and load. We push updates only for things a user is likely to act on:

  • Seat availability and price on charter departures the user is currently viewing.

  • Changes to a hotel allotment or rate within an open booking.

  • Status changes on a reservation, such as supplier confirmation.

For searches against external suppliers, we do not stream prices continuously. Instead we re-price at the booking step. Live updates are most useful for inventory we control, where we know exactly when something changed.

Broadcast an event when inventory changes

Start with an event that implements ShouldBroadcast. We broadcast on a channel scoped to the departure, so only clients viewing that departure receive it.

class DeparturePriceUpdated implements ShouldBroadcast
{
    public function __construct(
        public int $departureId,
        public int $seatsAvailable,
        public string $price,
        public string $currency,
    ) {}

    public function broadcastOn(): Channel
    {
        return new Channel("departures.{$this->departureId}");
    }
}

Dispatch the event after the database transaction that changed inventory has committed. If you broadcast inside the transaction and it rolls back, clients will see a change that never happened. Laravel supports this with the afterCommit property on the event, or by dispatching from an observer that runs after commit.

Prices should travel as strings or integer minor units, not floats. Rounding errors in a displayed fare cause support calls.

Use private channels for agency-specific prices

In a B2B platform, the same seat often has a different selling price for each agency because of markups and commissions. A public channel carrying one price would show the wrong number to most agents. There are two good options.

The first is to broadcast only that something changed, and let each client fetch its own price through an authenticated request. The second is to broadcast on private channels per agency, with the price already calculated. We use the first approach for low-frequency updates and the second where many agents watch the same departure.

Broadcast::channel('agency.{agencyId}', function ($user, $agencyId) {
    return $user->agency_id === (int) $agencyId;
});

Listen on the front end

With Livewire, a component can listen for the event and refresh the relevant data.

#[On('echo:departures.{departureId},DeparturePriceUpdated')]
public function refreshPrice(array $payload): void
{
    $this->seatsAvailable = $payload['seatsAvailable'];
    $this->price = $payload['price'];
    $this->priceChanged = true;
}

The priceChanged flag drives a short, visible notice next to the price. Silently changing a number on screen is confusing, especially if the agent has already quoted it to a customer. Telling them the price changed, and from what to what, is better practice.

Plan for disconnections and scale

WebSocket connections drop when laptops sleep or mobile networks change. When a client reconnects, it should fetch the current state rather than trusting that it missed nothing. We also keep the booking step authoritative: whatever the live feed showed, the server re-checks price and availability when the user confirms.

  • Run the WebSocket server on its own instances so connection load does not affect web requests.

  • Use Redis to share broadcast events across application servers.

  • Throttle updates on busy departures, sending the latest state at most once every few seconds.

Summary

Live price updates make a booking screen more trustworthy when they are targeted, correct and clearly signalled to the user. Broadcast after commit, scope channels to what the user is viewing, respect agency-specific pricing and keep the server as the final authority. The real-time layer then becomes a convenience on top of a correct system rather than a source of new problems.

More from the blog