# HubSpot et WPForms ensemble : faire correspondre les champs sans erreur

> Mettre en place une synchronisation fiable entre WPForms et HubSpot, avec un mécanisme de reprise quand l'API renvoie une erreur temporaire.

- Auteur : Clément Hadrot
- Publié le : 2024-07-24
- Mis à jour le : 2024-07-24
- Catégorie : Extensions
- URL : https://wpmoderne.dev.wordpress-developpement.fr/extensions/hubspot-wpforms-faire-correspondre-champs-sans-erreur/

## L’essentiel

- Mapper par nom de propriété interne, jamais par libellé affiché
- Une file d'attente locale absorbe les erreurs 429 et 5xx
- Chaque échec journalisé reste rejouable manuellement

`PROPERTY_DOESNT_EXIST` : ce code d'erreur renvoyé par l'API HubSpot revient régulièrement dans les journaux d'une extension de synchronisation de formulaires, souvent plusieurs semaines après une mise en production sans accroc apparent. La cause est presque toujours la même : un champ WPForms mappé sur le libellé affiché d'une propriété HubSpot, plutôt que sur son nom interne, qui a changé de son côté sans prévenir personne côté WordPress.

WPForms propose nativement une extension HubSpot dans sa version Pro, mais dès que le formulaire dépasse une poignée de champs simples — choix multiples, propriétés personnalisées, listes déroulantes dépendantes — une intégration sur mesure via l'API REST de HubSpot devient plus fiable que le mapping graphique fourni par défaut. Voici comment la construire sans reproduire les pièges classiques.

## Mapper par nom interne, pas par libellé

Chaque propriété HubSpot possède un nom interne stable (`internal_name`), distinct de son libellé affiché dans l'interface, qui peut lui être renommé à tout moment par un utilisateur métier sans notification technique. Le mapping entre les champs WPForms et les propriétés HubSpot doit toujours reposer sur ce nom interne, récupéré une fois via l'endpoint `/crm/v3/properties/contacts` et stocké dans les réglages de l'extension :

```
$response = wp_remote_get(
    'https://api.hubapi.com/crm/v3/properties/contacts',
    array(
        'headers' => array(
            'Authorization' => 'Bearer ' . $token,
        ),
        'timeout'  => 15,
    )
);
```

Cette liste de propriétés sert à construire un écran de réglages où l'administrateur associe chaque champ WPForms à un nom interne HubSpot, jamais à un libellé traduit en français qui n'a aucune existence côté API.

## Ce que la soumission déclenche réellement

> L'essentiel à retenir : Mapper par nom de propriété interne, jamais par libellé affiché ; Une file d'attente locale absorbe les erreurs 429 et 5xx ; Chaque échec journalisé reste rejouable manuellement

Le point d'accroche standard est le hook `wpforms_process_complete`, déclenché après validation d'une soumission mais avant l'affichage du message de confirmation. C'est là que l'extension construit le tableau de propriétés à envoyer, en utilisant le mapping stocké plutôt qu'une correspondance codée en dur pour un formulaire précis :

```
add_action( 'wpforms_process_complete', function( $fields, $entry, $form_data ) {
    $mapping = get_option( 'crm_sync_field_mapping', array() );
    $properties = array();

    foreach ( $fields as $field ) {
        $wpforms_id = $field['id'];
        if ( isset( $mapping[ $wpforms_id ] ) ) {
            $properties[ $mapping[ $wpforms_id ] ] = $field['value'];
        }
    }

    crm_sync_queue_contact( $entry['id'], $properties );
}, 10, 3 );
```

## Absorber les erreurs 429 et 5xx sans perdre la soumission

Envoyer directement l'appel à HubSpot depuis le hook de soumission est risqué : un dépassement de quota (erreur `429`) ou une indisponibilité temporaire de l'API renvoie une erreur au moment précis où le visiteur attend une confirmation. La bonne pratique consiste à ne jamais appeler l'API en synchrone depuis la requête HTTP du visiteur, mais à empiler la soumission dans une file d'attente traitée par Action Scheduler :

- La fonction `crm_sync_queue_contact()` écrit la soumission dans une table dédiée avec un statut `pending`.
- Une tâche planifiée via `as_schedule_single_action()` traite la file toutes les minutes.
- Sur une erreur `429` ou `5xx`, le statut repasse à `pending` avec un compteur de tentatives incrémenté, jusqu'à trois essais.
- Au-delà de trois échecs, le statut passe à `failed` et une notification apparaît dans l'écran d'administration de l'extension.

## Rejouer une synchronisation échouée sans ressaisir le formulaire

Chaque entrée de la file conserve le tableau de propriétés déjà construit, ce qui permet de rejouer manuellement une synchronisation échouée d'un clic depuis l'écran d'administration, sans redemander au visiteur de soumettre à nouveau son formulaire. C'est un détail qui change beaucoup l'expérience du service commercial côté client : un lead capturé n'est jamais perdu, même en cas de panne HubSpot de plusieurs heures.

> Conseil qui a évité plusieurs incidents : ne jamais logger le jeton d'API HubSpot dans les journaux d'erreur, même en cas d'échec. Un message d'erreur générique suffit à diagnostiquer, et un jeton qui traîne dans un fichier de log est un risque de sécurité inutile.

## Pour aller plus loin

Cette architecture — mapping par nom interne, file d'attente locale, reprise automatique — s'applique de la même façon à n'importe quel autre CRM disposant d'une API REST avec limitation de débit. La leçon générale dépasse HubSpot et WPForms : dès qu'une intégration tierce implique un appel réseau synchrone dans le parcours d'un visiteur, la découpler en file d'attente asynchrone protège autant l'expérience utilisateur que l'intégrité des données transmises.
