Des guides techniques avec du vrai code, pas des promesses en l'air. Chaque intégration couvre deux choses : afficher les résultats Heurix à la place de la recherche native, et garder votre catalogue synchronisé.
Honnêteté d'abord, et la réponse n'est pas la même selon la plateforme. PrestaShop a un module payant, vendu sur PrestaShop Addons depuis le 11 septembre 2026 — voir sa page. WooCommerce a une extension publiée sur wordpress.org, installable depuis l'administration WordPress — voir sa page. Shopify a une application, soumise à la revue de l'App Store le 13 septembre 2026, que Shopify n'a ni validée ni refusée à ce jour — voir sa page. Magento n'a que le guide ci-dessous, et le guide Shopify n'a pas été éprouvé sur une boutique réelle. Le module PrestaShop et l'extension WooCommerce s'installent aujourd'hui depuis le store officiel de leur plateforme, et le code qui suit reste la voie si vous préférez brancher la recherche vous-même. Ce que la réponse porte, et l'URL de fiche que votre front compose : la recherche headless. Corrigé le 28 septembre 2026 : cette note disait « seul le module PrestaShop s'installe depuis le store officiel de sa plateforme », vrai jusqu'à la publication de l'extension WooCommerce sur wordpress.org ce jour-là. Corrigé le 19 septembre 2026 : cette note disait depuis le 6 septembre que Shopify n'avait que le guide, et, après la validation du module PrestaShop sur Addons le 11 septembre, que ce module était en bêta et qu'aucun des quatre ne s'installait depuis un store officiel.
Vous exportez votre catalogue depuis un ERP ou un PIM ? Déposez le fichier dans votre console, en CSV ou en XML : la structure est détectée, la correspondance de champs proposée, le pack de règles recommandé d'après votre contenu. Voir la marche à suivre →
Le relais (Cloudflare Worker) :
// worker.js — déployé sur Cloudflare Workers (gratuit pour ce volume)
export default {
async fetch(request, env) {
const url = new URL(request.url);
const q = url.searchParams.get("q") || "";
const res = await fetch("https://api.heurix.fr/v1/index/" + env.HEURIX_CATALOG + "/search", {
method: "POST",
headers: {
"Authorization": "Bearer " + env.HEURIX_API_KEY, // secret Cloudflare, jamais exposé
"Content-Type": "application/json"
},
body: JSON.stringify({ q, limit: 20 })
});
return new Response(await res.text(), {
headers: { "Content-Type": "application/json", "Access-Control-Allow-Origin": "https://votre-boutique.myshopify.com" }
});
}
}
Dans le thème (Online Store 2.0), remplacez le rendu de sections/search-results.liquid par un appel à ce relais :
<div id="heurix-results"></div>
<script>
const params = new URLSearchParams(window.location.search);
fetch("https://votre-worker.workers.dev/search?q=" + encodeURIComponent(params.get("q") || ""))
.then(r => r.json())
.then(data => {
document.getElementById("heurix-results").innerHTML = data.hits.map(h =>
`<a href="/products/${h.product.handle}" class="heurix-hit">
<span>${h.product.name}</span><span>${h.product.price} €</span>
</a>`
).join("");
});
</script>
Enregistrez un webhook Shopify (Admin API ou shopify.app.toml pour une app custom) sur les sujets products/create, products/update, products/delete, pointé vers le même Worker :
// Ajout dans worker.js : gestion du webhook Shopify
async function handleWebhook(request, env) {
const product = await request.json();
await fetch("https://api.heurix.fr/v1/index/" + env.HEURIX_CATALOG + "/items", {
method: "POST",
headers: { "Authorization": "Bearer " + env.HEURIX_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
rulepack: "mode",
items: [{
id: String(product.id),
name: product.title,
ref: product.variants?.[0]?.sku || "",
description: product.body_html?.replace(/<[^>]+>/g, "") || "",
stock: product.variants?.reduce((s, v) => s + (v.inventory_quantity || 0), 0) || 0,
handle: product.handle,
price: product.variants?.[0]?.price
}]
})
});
}
Pensez à vérifier la signature HMAC du webhook Shopify (X-Shopify-Hmac-Sha256) avant de traiter la charge utile, pour ne pas accepter de requêtes forgées.
Dans un module custom, on hook displaySearchResults ou on surcharge le contrôleur de recherche :
<?php
// modules/heurixsearch/heurixsearch.php
class HeurixSearch extends Module
{
public function search($query)
{
$ch = curl_init('https://api.heurix.fr/v1/index/' . Configuration::get('HEURIX_CATALOG') . '/search');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . Configuration::get('HEURIX_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['q' => $query, 'limit' => 24]),
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
return $response['hits'] ?? [];
}
}
Dans le template de résultats (search.tpl), on boucle sur ces résultats à la place de la recherche native PrestaShop.
Trois hooks suffisent pour rester à jour en temps réel :
<?php
public function install()
{
return parent::install()
&& $this->registerHook('actionProductAdd')
&& $this->registerHook('actionProductUpdate')
&& $this->registerHook('actionProductDelete');
}
public function hookActionProductUpdate($params)
{
$product = $params['product'];
$this->pushToHeurix([
'id' => (string) $product->id,
'name' => $product->name,
'ref' => $product->reference,
'description' => strip_tags($product->description_short),
'stock' => (int) StockAvailable::getQuantityAvailableByProduct($product->id),
]);
}
private function pushToHeurix($item)
{
$ch = curl_init('https://api.heurix.fr/v1/index/' . Configuration::get('HEURIX_CATALOG') . '/items');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . Configuration::get('HEURIX_API_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode(['rulepack' => 'mode', 'items' => [$item]]),
]);
curl_exec($ch);
curl_close($ch);
}
wp_remote_post(), la fonction HTTP native de WordPress, plutôt que du cURL brut.
<?php
// Dans functions.php ou un plugin dédié
add_filter('woocommerce_product_query_meta_query', function ($meta_query, $query) {
if (!is_search()) return $meta_query;
$response = wp_remote_post('https://api.heurix.fr/v1/index/' . HEURIX_CATALOG . '/search', [
'headers' => [
'Authorization' => 'Bearer ' . HEURIX_API_KEY,
'Content-Type' => 'application/json',
],
'body' => json_encode(['q' => get_search_query(), 'limit' => 24]),
]);
$hits = json_decode(wp_remote_retrieve_body($response), true)['hits'] ?? [];
$ids = array_map(fn($h) => (int) $h['product']['id'], $hits);
// Réordonne les résultats WooCommerce natifs selon le classement Heurix
$query->set('post__in', $ids ?: [0]);
$query->set('orderby', 'post__in');
return $meta_query;
}, 10, 2);
Cette approche a un avantage : elle réutilise l'affichage natif WooCommerce (fiches produit, filtres existants), Heurix ne fait que reclasser les résultats — moins de template à réécrire.
<?php
add_action('woocommerce_update_product', 'heurix_sync_product');
add_action('woocommerce_new_product', 'heurix_sync_product');
function heurix_sync_product($product_id) {
$product = wc_get_product($product_id);
wp_remote_post('https://api.heurix.fr/v1/index/' . HEURIX_CATALOG . '/items', [
'headers' => [
'Authorization' => 'Bearer ' . HEURIX_API_KEY,
'Content-Type' => 'application/json',
],
'body' => json_encode([
'rulepack' => 'mode',
'items' => [[
'id' => (string) $product_id,
'name' => $product->get_name(),
'ref' => $product->get_sku(),
'description' => wp_strip_all_tags($product->get_short_description()),
'stock' => $product->get_stock_quantity() ?? 0,
]],
]),
]);
}
add_action('woocommerce_delete_product', function ($product_id) {
wp_remote_request('https://api.heurix.fr/v1/index/' . HEURIX_CATALOG . '/items/' . $product_id, [
'method' => 'DELETE',
'headers' => ['Authorization' => 'Bearer ' . HEURIX_API_KEY],
]);
});
Un module custom avec un plugin sur le bloc de résultats de recherche :
<?php
// app/code/Heurix/Search/Model/SearchClient.php
namespace Heurix\Search\Model;
use Magento\Framework\HTTP\Client\Curl;
class SearchClient
{
public function __construct(private Curl $curl) {}
public function search(string $query): array
{
$this->curl->addHeader('Authorization', 'Bearer ' . HEURIX_API_KEY);
$this->curl->addHeader('Content-Type', 'application/json');
$this->curl->post(
'https://api.heurix.fr/v1/index/' . HEURIX_CATALOG . '/search',
json_encode(['q' => $query, 'limit' => 24])
);
$data = json_decode($this->curl->getBody(), true);
return $data['hits'] ?? [];
}
}
Injectez ce service dans un plugin sur Magento\CatalogSearch\Model\Layer\Filter ou remplacez directement le template de résultats de recherche par un rendu basé sur ces données.
Un observer sur les événements standards du catalogue :
<?php
// app/code/Heurix/Search/Observer/SyncProduct.php
namespace Heurix\Search\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Event\Observer;
class SyncProduct implements ObserverInterface
{
public function __construct(private \Magento\Framework\HTTP\Client\Curl $curl) {}
public function execute(Observer $observer)
{
$product = $observer->getEvent()->getProduct();
$this->curl->addHeader('Authorization', 'Bearer ' . HEURIX_API_KEY);
$this->curl->addHeader('Content-Type', 'application/json');
$this->curl->post('https://api.heurix.fr/v1/index/' . HEURIX_CATALOG . '/items', json_encode([
'rulepack' => 'mode',
'items' => [[
'id' => (string) $product->getId(),
'name' => $product->getName(),
'ref' => $product->getSku(),
'stock' => (int) $product->getExtensionAttributes()?->getStockItem()?->getQty(),
]],
]));
}
}
Déclaré dans events.xml sur catalog_product_save_after et catalog_product_delete_after.
Ces guides sont un point de départ technique, pas un module clé en main. Si votre configuration sort de l'ordinaire, écrivez-nous.
Testez Heurix sur votre catalogue, sans carte bancaire.
Démarrer l'essai gratuit