Est-il possible de s’inscrire à un événement payant sans jamais payer ? Sur le site d’un club nautique organisant chaque année une régate ouverte aux plaisanciers extérieurs, la réponse s’est révélée oui, pendant plusieurs semaines, pour dix-sept participants qui ont ainsi pu réserver leur place sans que le paiement ne soit jamais réellement débité. La billetterie de l’événement elle-même, son interface de réservation de créneaux, ne fait pas l’objet de cet article : le problème se situait dans l’articulation entre le formulaire d’inscription et la confirmation du paiement.
Le parcours d’inscription fonctionnait ainsi : un participant remplissait un formulaire, était redirigé vers l’interface de paiement du prestataire, puis revenait sur une page de confirmation du site du club une fois le paiement effectué. Le problème tenait à cette page de retour : elle validait l’inscription dès son affichage, en se basant uniquement sur un paramètre présent dans l’URL de redirection, sans jamais vérifier auprès du prestataire de paiement que la transaction avait réellement abouti.
Le mécanisme exact de la faille
L’URL de retour, après paiement, ressemblait à /confirmation-regate/?statut=paye&inscription=482. Le code de la page, en constatant la présence de statut=paye dans l’URL, marquait directement l’inscription comme validée en base de données, sans appeler l’API du prestataire de paiement pour confirmer que la transaction correspondante existait bel et bien et avait le statut attendu. Modifier manuellement ce paramètre dans la barre d’adresse du navigateur, avant même d’avoir entré un moyen de paiement, suffisait à obtenir la même confirmation.
Pourquoi ce type de faille échappe souvent aux tests classiques

Un scanner de vulnérabilités automatisé, qui recherche des failles techniques comme des injections ou des failles XSS, ne détecte jamais ce genre de problème : le code ne contient aucune erreur de syntaxe, aucune injection, aucun défaut d’échappement. La faille est purement logique, elle réside dans une hypothèse implicite (« si l’utilisateur revient sur cette page avec ce paramètre, c’est qu’il a payé ») qui ne correspond à aucune vérification technique réelle.
Le correctif : vérifier le paiement côté serveur, jamais côté client
function confirmer_inscription_regate( $inscription_id, $reference_transaction ) {
$transaction = api_prestataire_paiement_recuperer( $reference_transaction );
if ( ! $transaction || $transaction['statut'] !== 'reussie' ) {
return new WP_Error( 'paiement_non_confirme', 'Le paiement n\'a pas pu être vérifié.' );
}
if ( (float) $transaction['montant'] !== (float) get_montant_attendu( $inscription_id ) ) {
return new WP_Error( 'montant_incorrect', 'Le montant ne correspond pas à l\'inscription.' );
}
update_post_meta( $inscription_id, 'statut_paiement', 'confirme' );
return true;
}
La vérification se fait désormais systématiquement via un appel serveur à serveur vers l’API du prestataire, en fournissant la référence de transaction, et en comparant le montant réellement débité au montant attendu pour l’inscription concernée. La page de retour du navigateur ne sert plus qu’à afficher un message, elle ne déclenche plus aucune validation par elle-même.
Ajouter une confirmation par webhook, en complément
Pour se prémunir d’un cas où le visiteur fermerait son navigateur avant même d’atteindre la page de retour, un webhook envoyé directement par le prestataire de paiement, indépendant de tout comportement du navigateur, déclenche la même vérification de façon asynchrone et fiable.
Régulariser les dix-sept inscriptions concernées
Une fois la faille corrigée, le club a dû traiter individuellement les dix-sept inscriptions validées sans paiement réel, en recontactant chaque participant pour régulariser sa situation avant le jour de la régate, une démarche délicate mais nécessaire pour ne pas pénaliser ceux ayant simplement profité d’un lien partagé sans intention frauduleuse.
Un statut aussi déterminant qu’un paiement ne se lit jamais dans un paramètre d’URL contrôlé par le navigateur : il se vérifie toujours par un appel direct, serveur à serveur, vers la source qui fait foi.
En résumé
Cette faille de logique métier n’avait rien de sophistiqué techniquement : modifier un paramètre d’URL suffisait. Elle illustre un principe général qui dépasse largement ce cas précis : toute étape qui engage de l’argent réel doit être confirmée par une source faisant autorité, jamais par ce que le navigateur du visiteur affirme.