Le transporteur retenu par ce client dispose bien d’une API de recherche de points relais, mais aucune extension WooCommerce ne l’exploite : les modules disponibles sur le marché ciblent d’autres réseaux plus connus. Il a donc fallu construire l’intégration à la main, sans reposer sur une extension tierce. L’exercice reste instructif au-delà de ce cas précis, tant le schéma se retrouve à l’identique dès qu’un transporteur moins courant doit s’intégrer au tunnel de commande.
Le tarif de livraison lui-même restait fixe pour ce projet, calculé par ailleurs via une méthode d’expédition classique ; seule la sélection du point relais devait s’ajouter à l’étape de livraison, avec un identifiant transmis ensuite au transporteur au moment de la création de l’étiquette.
Étape 1 : préparer un point d’entrée AJAX côté serveur
Premier réflexe : ne jamais appeler l’API du transporteur directement depuis le navigateur du client, pour ne pas exposer la clé d’authentification de l’API dans le code source de la page. Un point d’entrée AJAX WordPress classique sert d’intermédiaire :
add_action( 'wp_ajax_pr_rechercher_points_relais', 'pr_rechercher_points_relais' );
add_action( 'wp_ajax_nopriv_pr_rechercher_points_relais', 'pr_rechercher_points_relais' );
function pr_rechercher_points_relais() {
check_ajax_referer( 'pr_nonce', 'nonce' );
$code_postal = sanitize_text_field( $_POST['code_postal'] ?? '' );
if ( empty( $code_postal ) ) {
wp_send_json_error( 'Code postal manquant' );
}
$reponse = wp_remote_get( 'https://api.transporteur-exemple.fr/points-relais?cp=' . urlencode( $code_postal ), array(
'headers' => array( 'Authorization' => 'Bearer ' . PR_API_TOKEN ),
'timeout' => 8,
) );
if ( is_wp_error( $reponse ) ) {
wp_send_json_error( 'Service transporteur indisponible' );
}
$points = json_decode( wp_remote_retrieve_body( $reponse ), true );
wp_send_json_success( $points );
}
Étape 2 : injecter le script et le nonce dans la page de commande

Le script front-end ne doit s’enqueuer que sur la page de commande, jamais sur l’ensemble du site :
add_action( 'wp_enqueue_scripts', 'pr_enqueue_script_checkout' );
function pr_enqueue_script_checkout() {
if ( ! is_checkout() ) {
return;
}
wp_enqueue_script( 'pr-points-relais', get_stylesheet_directory_uri() . '/js/points-relais.js', array( 'jquery' ), '1.0', true );
wp_localize_script( 'pr-points-relais', 'prConfig', array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'pr_nonce' ),
) );
}
Étape 3 : afficher le sélecteur sous la méthode de livraison
Le hook woocommerce_review_order_before_payment permet d’insérer un bloc HTML juste avant les moyens de paiement, à l’endroit où le client s’attend logiquement à choisir un point de retrait :
add_action( 'woocommerce_review_order_before_payment', 'pr_afficher_selecteur' );
function pr_afficher_selecteur() {
echo '<div id="pr-selecteur-relais">';
echo '<input type="text" id="pr-code-postal" placeholder="Votre code postal" />';
echo '<div id="pr-liste-resultats"></div>';
echo '<input type="hidden" name="pr_point_relais_id" id="pr_point_relais_id" />';
echo '</div>';
}
Le script JavaScript, appelé au clic sur un point relais de la liste retournée, remplit le champ caché pr_point_relais_id, qui sera transmis avec le reste du formulaire de commande au moment de la validation.
Étape 4 : valider côté serveur avant d’accepter la commande
Un point critique, souvent oublié : sans contrôle serveur, rien n’empêche un client de valider sa commande sans avoir sélectionné de point relais, en particulier si JavaScript échoue à charger. Le hook woocommerce_after_checkout_validation bloque ce cas :
add_action( 'woocommerce_after_checkout_validation', 'pr_valider_point_relais_choisi', 10, 2 );
function pr_valider_point_relais_choisi( $data, $errors ) {
if ( empty( $_POST['pr_point_relais_id'] ) ) {
$errors->add( 'validation', 'Veuillez sélectionner un point relais avant de valider votre commande.' );
}
}
Étape 5 : enregistrer le point relais dans la commande
Une fois la commande validée, l’identifiant du point relais doit être conservé pour être transmis au transporteur lors de la création de l’étiquette d’expédition :
add_action( 'woocommerce_checkout_update_order_meta', 'pr_enregistrer_point_relais' );
function pr_enregistrer_point_relais( $order_id ) {
if ( ! empty( $_POST['pr_point_relais_id'] ) ) {
update_post_meta( $order_id, '_pr_point_relais_id', sanitize_text_field( $_POST['pr_point_relais_id'] ) );
}
}
Cette métadonnée devient ensuite consultable depuis l’écran de détail de commande, via un ajout dans woocommerce_admin_order_data_after_shipping_address, pour que l’équipe logistique voie immédiatement quel point relais préparer sans ouvrir un outil externe.
Points de vigilance
- Toujours fixer un
timeoutraisonnable surwp_remote_get(): une API transporteur lente ne doit jamais faire attendre la page de commande indéfiniment. - Prévoir un message clair en cas d’échec de l’API, plutôt qu’une liste vide silencieuse qui laisse penser à une erreur de saisie du code postal.
- Ne jamais stocker la clé d’API du transporteur ailleurs que dans une constante définie en dehors du dépôt de code versionné.
Un sélecteur de point relais maison n’a rien d’exotique techniquement : c’est une recherche AJAX classique, un champ caché, et une validation serveur qui empêche de la contourner. La difficulté tient surtout à la discipline sur la sécurité de la clé d’API.
En résumé
Faute d’extension prête à l’emploi pour ce transporteur, la construction sur mesure de ce sélecteur de point relais a demandé une journée de développement, contrôles inclus. Le schéma proposé ici, point d’entrée AJAX, validation serveur systématique et enregistrement en métadonnée de commande, se réutilise ensuite tel quel pour n’importe quel autre transporteur dépourvu d’extension WooCommerce officielle.