Apparence
Holds et sessions
Le problème
Trois jetons circulent — sessionToken, holdToken, eventId — et il n'est pas évident de savoir lequel va où, qui pose les holds, ni ce qui se passe à l'expiration. Cette page répond à ces trois questions.
Les trois jetons
| Jeton | Produit par | Vit où | Sert à |
|---|---|---|---|
sessionToken | sessions.create() ou le widget | Navigateur | Identifier la session widget, appeler /api/widget/** |
holdToken | sessions.create() ou le widget | Serveur | Confirmer, libérer, prolonger les sièges du panier |
eventId | Résolution du slug | Les deux | Cibler l'événement |
Le holdToken ne va pas au navigateur
C'est lui qui autorise book() et release(). Exposé côté client, il permettrait de manipuler le panier d'un autre. Créez la session côté serveur, envoyez le sessionToken à la page, gardez le holdToken.
Si le widget crée lui-même la session, vous récupérez les deux jetons dans onReady. Dans ce cas, remontez immédiatement le holdToken à votre serveur et n'en gardez pas de copie côté page.
Qui pose les holds
| Scénario | Qui bloque | Comment |
|---|---|---|
| L'utilisateur clique un siège dans le widget | Le widget | Automatique, à chaque sélection |
| Vente au guichet depuis votre back-office | Vous | holds.hold(eventId, seats, holdToken) |
| Quota réservé à un partenaire | Vous | holds.changeStatus(eventId, seats, 'hold') |
| Import d'une billetterie externe | Vous | holds.book(...) directement |
Désélectionner un siège dans le widget retire son hold. Vous n'avez rien à faire pour ce cas.
Durée de vie
La durée d'un hold est un réglage de workspace : back-office → Paramètres → Durée de hold. Elle s'applique à tous les holds, ceux du widget comme les vôtres.
Chaque hold() renvoie la durée effective :
ts
const hold = await client.holds.hold(eventId, ['A-1'], holdToken);
hold.expiresAt; // Date — quand le siège redevient libre
hold.durationSeconds; // number — la durée configuréephp
$hold = $client->holds->hold($eventId, ['A-1'], $holdToken);
$hold->expiresAt; // DateTimeImmutable
$hold->durationSeconds; // intServez-vous de expiresAt pour afficher un compte à rebours : un utilisateur qui voit « il vous reste 7:30 pour finaliser » abandonne moins qu'un utilisateur surpris par une place perdue.
Prolonger
Un tunnel de paiement plus long que la durée de hold ? Rafraîchissez la session avant la redirection :
ts
const renewed = await client.sessions.refresh(sessionToken);
renewed.expiresIn; // nouvelle durée, en secondesphp
$renewed = $client->sessions->refresh($sessionToken);
$renewed->expiresIn; // nouvelle durée, en secondesAlternative : reposer un hold() sur les mêmes sièges avec le même holdToken, ce qui repart de zéro.
Ce qui se passe à l'expiration
Les sièges repassent en available, silencieusement. Il n'y a ni webhook ni notification — c'est à votre application de le constater.
Deux conséquences pratiques :
Avant de confirmer, ne présumez rien. Un book() sur un hold expiré échoue en 409 si quelqu'un a pris la place entre-temps, avec le message One or more seats are not available. C'est le cas nominal, à traiter — voir Brancher un paiement.
Faites le ménage dans vos commandes. Une tâche périodique qui repasse les commandes en_attente trop anciennes en expiree évite d'accumuler des paniers fantômes de votre côté.
Vérifier l'état réel
En cas de doute, la source de vérité est l'événement :
ts
const map = await client.events.listSeats(eventId, ['A-1', 'A-2']);
map.held(); // ['A-1'] → hold encore actif
map.available(); // ['A-2'] → hold expiré ou jamais posé
map.isAvailable('A-2'); // truephp
$map = $client->events->listSeats($eventId, ['A-1', 'A-2']);
$map->held(); // ['A-1']
$map->available(); // ['A-2']
$map->isAvailable('A-2'); // truePasser la liste des sièges en second argument évite de rapatrier une salle entière pour vérifier deux places.
Erreurs fréquentes
| Symptôme | Cause | Correctif |
|---|---|---|
book() renvoie 409 alors que l'utilisateur avait bien sélectionné | Hold expiré pendant le paiement | Rembourser, prévenir, proposer de nouvelles places |
| Les sièges restent bloqués après un abandon | Aucun release() appelé | Libérer sur la route d'annulation du PSP |
holdToken invalide | Session expirée ou jeton d'une autre session | Recréer une session |
| Deux onglets, deux paniers | Deux sessions distinctes, holds séparés | Rattacher la session Mitoera à la session applicative |
À retenir
sessionToken | Navigateur |
holdToken | Serveur, jamais exposé |
| Le widget pose les holds | Automatiquement, à la sélection |
| Durée | Réglage de workspace, lisible dans hold.durationSeconds |
| Expiration | Silencieuse, sans notification |
| Vérification | events.listSeats() fait foi |

