# Verrouiller un stock de chambres d’hôtel sans jamais le survendre

> Deux visiteurs qui réservent la même chambre à quelques secondes d'intervalle peuvent tous deux obtenir une confirmation. Voici comment une extension bloque la disponibilité le temps du paiement.

- Auteur : Clément Hadrot
- Publié le : 2024-03-10
- Mis à jour le : 2024-03-10
- Catégorie : Extensions
- URL : https://wpmoderne.dev.wordpress-developpement.fr/extensions/hotel-independant-verrouiller-stock-chambres/

## L’essentiel

- Un verrou MySQL nommé bloque la vérification de disponibilité pendant le paiement
- La libération automatique du verrou évite un blocage permanent en cas d'échec
- Une table de disponibilité par chambre et par nuit, pas un simple compteur global

Comment empêcher deux visiteurs de réserver la même chambre pour la même nuit quand ils valident leur paiement à quelques secondes d'intervalle ? C'est le problème classique de la survente, particulièrement sensible pour un hôtel indépendant qui gère lui-même son moteur de réservation sans channel manager reliant plusieurs plateformes entre elles. Ce tutoriel construit, étape par étape, un dispositif de verrouillage temporaire qui bloque une chambre le temps exact du paiement.

## Étape 1 : modéliser la disponibilité par chambre et par nuit

Un simple compteur global de chambres disponibles ne suffit pas : il faut savoir précisément quelle chambre est libre pour quelle nuit. Une table personnalisée, avec une ligne par chambre et par date, sert de source de vérité :

```
CREATE TABLE wp_hotel_disponibilite (
    id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
    chambre_id BIGINT UNSIGNED NOT NULL,
    nuit DATE NOT NULL,
    statut ENUM('libre','bloquee','reservee') NOT NULL DEFAULT 'libre',
    expiration_blocage DATETIME NULL,
    PRIMARY KEY (id),
    UNIQUE KEY chambre_nuit (chambre_id, nuit)
);
```

## Étape 2 : verrouiller la vérification avec GET_LOCK

> L'essentiel à retenir : Un verrou MySQL nommé bloque la vérification de disponibilité pendant le paiement ; La libération automatique du verrou évite un blocage permanent en cas d'échec ; Une table de disponibilité par chambre et par nuit, pas un simple compteur global

La vraie difficulté n'est pas la mise à jour du statut, mais la vérification de disponibilité suivie de son changement : deux requêtes concurrentes peuvent toutes deux lire « libre » avant que l'une des deux n'écrive « bloquée ». La fonction MySQL `GET_LOCK()`, appelée via `$wpdb->get_var()`, sérialise cette séquence lire-puis-écrire pour une chambre et une nuit données :

```
function hotel_bloquer_chambre( $chambre_id, $nuit ) {
    global $wpdb;
    $cle = "chambre_{$chambre_id}_{$nuit}";

    $obtenu = $wpdb->get_var( $wpdb->prepare( 'SELECT GET_LOCK(%s, 5)', $cle ) );

    if ( '1' !== $obtenu ) {
        return new WP_Error( 'verrou_indisponible', 'Impossible de vérifier la disponibilité pour le moment.' );
    }

    $statut = $wpdb->get_var( $wpdb->prepare(
        "SELECT statut FROM {$wpdb->prefix}hotel_disponibilite WHERE chambre_id = %d AND nuit = %s",
        $chambre_id, $nuit
    ) );

    if ( 'libre' !== $statut ) {
        $wpdb->query( $wpdb->prepare( 'SELECT RELEASE_LOCK(%s)', $cle ) );
        return new WP_Error( 'chambre_indisponible', 'Cette chambre n’est plus disponible pour cette nuit.' );
    }

    $wpdb->update( $wpdb->prefix . 'hotel_disponibilite', array(
        'statut'             => 'bloquee',
        'expiration_blocage' => gmdate( 'Y-m-d H:i:s', strtotime( '+15 minutes' ) ),
    ), array( 'chambre_id' => $chambre_id, 'nuit' => $nuit ) );

    $wpdb->query( $wpdb->prepare( 'SELECT RELEASE_LOCK(%s)', $cle ) );

    return true;
}
```

Le verrou `GET_LOCK` n'est tenu que pendant la vérification et l'écriture du statut, quelques millisecondes, jamais pendant tout le processus de paiement qui peut durer plusieurs minutes.

## Étape 3 : confirmer ou libérer selon l'issue du paiement

Une fois le paiement confirmé par le prestataire choisi, le statut passe à `reservee` de façon définitive. En cas d'échec ou d'abandon du paiement, la chambre doit redevenir disponible sans attendre l'expiration du blocage :

```
function hotel_confirmer_reservation( $chambre_id, $nuit ) {
    global $wpdb;
    $wpdb->update( $wpdb->prefix . 'hotel_disponibilite',
        array( 'statut' => 'reservee', 'expiration_blocage' => null ),
        array( 'chambre_id' => $chambre_id, 'nuit' => $nuit )
    );
}

function hotel_liberer_chambre( $chambre_id, $nuit ) {
    global $wpdb;
    $wpdb->update( $wpdb->prefix . 'hotel_disponibilite',
        array( 'statut' => 'libre', 'expiration_blocage' => null ),
        array( 'chambre_id' => $chambre_id, 'nuit' => $nuit )
    );
}
```

## Étape 4 : purger les blocages expirés par tâche planifiée

Un visiteur qui ferme son navigateur en plein paiement laisse une chambre bloquée sans jamais la libérer explicitement. Une tâche planifiée, déclenchée toutes les cinq minutes via `wp_schedule_event()`, corrige automatiquement cet oubli :

```
add_action( 'hotel_purger_blocages', function() {
    global $wpdb;
    $wpdb->query(
        "UPDATE {$wpdb->prefix}hotel_disponibilite
         SET statut = 'libre', expiration_blocage = NULL
         WHERE statut = 'bloquee' AND expiration_blocage < NOW()"
    );
} );
```

Ce filet de sécurité garantit qu'aucune chambre ne reste indéfiniment bloquée à cause d'un paiement jamais finalisé, sans exiger d'action manuelle de la réception.

## Ce que ce dispositif ne couvre pas

Cette mécanique protège la disponibilité au sein d'un seul canal de réservation, celui du site de l'hôtel. Elle ne synchronise pas automatiquement le stock avec d'autres plateformes de réservation externes : cette synchronisation multi-plateformes relève d'un channel manager dédié, hors du périmètre d'une extension maison.

## Notre verdict

Pour un hôtel indépendant qui vend directement depuis son propre site, ce dispositif de verrouillage temporaire, appuyé sur `GET_LOCK()` et une purge planifiée, offre une garantie solide contre la survente sans nécessiter d'infrastructure lourde. Le choix d'une durée de blocage de quinze minutes reste ajustable selon le temps moyen constaté pour finaliser un paiement sur le site concerné.
