POST /wp-json/paiements/v1/mollie-webhook HTTP/1.1
Content-Type: application/x-www-form-urlencoded
id=tr_WDqYK6vllg
Voilà, dans son intégralité, ce que Mollie envoie lorsqu’un paiement change de statut : un unique champ id, sans montant, sans statut, sans signature à vérifier. Pour un indépendant qui encaisse des acomptes ou des prestations ponctuelles en dehors de toute boutique WooCommerce, cette sobriété surprend souvent au premier abord, et pousse certains développeurs à valider la commande sur simple réception de l’appel. C’est précisément l’erreur à ne pas commettre.
Le principe de fonctionnement de Mollie est volontairement minimaliste : le webhook ne sert qu’à signaler qu’un événement s’est produit sur un paiement donné. C’est à l’extension d’aller ensuite interroger l’API Mollie pour connaître l’état réel de ce paiement, avec la clé secrète du compte. Cette étape supplémentaire n’est pas une option, c’est le mécanisme de sécurité central de l’intégration.
Déclarer un point de terminaison REST dédié
Plutôt que de passer par admin-post.php, un contrôleur REST personnalisé convient mieux ici, car Mollie s’attend à interroger une URL stable, sans nonce ni cookie de session :
add_action( 'rest_api_init', function() {
register_rest_route( 'paiements/v1', '/mollie-webhook', array(
'methods' => 'POST',
'callback' => 'paiements_traiter_webhook_mollie',
'permission_callback' => '__return_true',
) );
} );
Le permission_callback renvoyant systématiquement true n’est pas un oubli de sécurité : Mollie n’envoie ni clé API ni signature dans l’appel, la vérification se fait en sens inverse, depuis WordPress vers Mollie.
Revérifier le paiement auprès de l’API Mollie

Dès réception de l’identifiant, l’extension doit interroger l’API Mollie en lecture, avec la clé secrète stockée côté serveur, jamais côté client :
function paiements_traiter_webhook_mollie( WP_REST_Request $request ) {
$id = sanitize_text_field( $request->get_param( 'id' ) );
if ( empty( $id ) ) {
return new WP_REST_Response( null, 200 );
}
$reponse = wp_remote_get( 'https://api.mollie.com/v2/payments/' . $id, array(
'headers' => array( 'Authorization' => 'Bearer ' . paiements_cle_secrete_mollie() ),
'timeout' => 10,
) );
if ( is_wp_error( $reponse ) ) {
error_log( 'Mollie : échec de vérification pour ' . $id . ' : ' . $reponse->get_error_message() );
return new WP_REST_Response( null, 200 );
}
$paiement = json_decode( wp_remote_retrieve_body( $reponse ), true );
paiements_appliquer_statut( $paiement );
return new WP_REST_Response( null, 200 );
}
Le champ status de la réponse Mollie prend l’une des valeurs open, pending, paid, failed, canceled ou expired. Seul un statut paid doit déclencher la validation de la prestation côté WordPress.
Toujours répondre 200, quel que soit le résultat
C’est le point le plus souvent mal compris : que le paiement soit validé, refusé ou introuvable, le point de terminaison doit renvoyer un code HTTP 200. Si WordPress renvoie une erreur 4xx ou 5xx, Mollie considère que la notification n’a pas été reçue et la renvoie selon son propre calendrier de nouvelles tentatives, ce qui peut multiplier les appels et compliquer le diagnostic sans rien apporter côté fiabilité.
Idempotence : traiter deux fois le même identifiant sans double effet
Comme Mollie peut renvoyer plusieurs fois le même identifiant de paiement, la fonction paiements_appliquer_statut() doit être idempotente : avant de déclencher un envoi d’email de confirmation ou une mise à jour de statut, elle vérifie que ce n’est pas déjà fait, via une métadonnée dédiée sur l’enregistrement de commande interne :
function paiements_appliquer_statut( $paiement ) {
$commande_id = get_option( 'mollie_ref_' . $paiement['id'] );
if ( 'paid' !== $paiement['status'] ) {
return;
}
if ( 'oui' === get_post_meta( $commande_id, 'paiement_confirme', true ) ) {
return;
}
update_post_meta( $commande_id, 'paiement_confirme', 'oui' );
wp_mail( get_post_meta( $commande_id, 'email_client', true ), 'Paiement confirmé', 'Merci, votre paiement a bien été reçu.' );
}
Ce que ce dispositif ne couvre pas
Cette mécanique ne traite que le paiement unique. Les paiements récurrents nécessitent l’API des mandats et des abonnements de Mollie, avec un cycle de vie et des statuts distincts, qui sortent du cadre d’une extension légère de ce type.
Pour aller plus loin
Pour un indépendant qui n’a pas besoin de WooCommerce, ce dispositif tient dans une extension d’une centaine de lignes : une route REST, un appel de vérification, et une méthode d’application idempotente. La discipline à retenir tient en une phrase : ne jamais faire confiance au contenu du webhook, seulement à ce que l’API Mollie confirme en retour.