Costruire un flusso di integrazione tra Akeneo PIM e PrestaShop
1. Introduzione
Questo documento spiega come trasferire i dati prodotto da Akeneo PIM a PrestaShop usando la piattaforma di integrazione Flowlyze. Poiché su Flowlyze non esiste un nodo di destinazione nativo per PrestaShop, l'integrazione viene realizzata con una richiesta HTTP personalizzata. Il flusso alimenta direttamente un ambiente PrestaShop, automatizzando completamente la gestione del catalogo prodotti.
Video tutorial
💡 Suggerimento: Guarda il video qui sotto per vedere il flusso passo passo.
2. Prerequisiti
- Un account Akeneo PIM attivo con accesso alle API.
- Un'installazione PrestaShop accessibile via HTTP.
- Una Webservice Key generata dal pannello di amministrazione di PrestaShop (necessaria per la Basic Auth).
- Un file
image_proxy.phpposizionato nella directory root di PrestaShop per gestire le operazioni di download/upload. - Un account Flowlyze attivo.
- Conoscenza intermedia di C# per la formattazione dei dati e la costruzione dell'XML.
3. Architettura dell'integrazione
Per aggirare i colli di bottiglia di performance e i limiti della sandbox C#, il flusso dati avviene nelle seguenti fasi:
- Trigger: Flowlyze si connette ad Akeneo e recupera i dati grezzi dei prodotti.
- Mapping: I dati grezzi di Akeneo vengono mappati sui campi core di PrestaShop usando il nodo Mapping di Flowlyze.
- Trasformazione e creazione prodotto (controller C#): Lo script C# elabora i dati JSON, costruisce il payload XML e lo invia a PrestaShop. Il prodotto viene salvato nel database (senza immagini).
- Destinazione HTTP: Poiché non esiste un adapter nativo per PrestaShop, il prodotto viene inviato con una destinazione HTTP.
- Trasferimento immagini (PHP Proxy): Lo script C# estrae il nuovo Product ID dalla risposta HTTP 201 di successo. Invia questo ID e i codici degli asset immagine di Akeneo come payload JSON al file
image_proxy.php. Il file PHP scarica le immagini e le carica sull'API PrestaShop.
4. Implementazione passo passo
Passo 4.1: Configurare il trigger su Akeneo PIM
Per prima cosa, stabilisci una connessione sicura alla sorgente dati:
- Nell'area di lavoro di Flowlyze, aggiungi un nuovo nodo Source e seleziona Akeneo.
- Autentica la connessione con le credenziali API di Akeneo (Client ID, Secret, Username, Password).
- Definisci quali cataloghi prodotto recuperare e la frequenza del trigger (schedulata o manuale).
Passo 4.2: Trasformazione e mapping dei dati (Flowlyze)
La struttura dati accettata da PrestaShop è più specifica rispetto ad altre piattaforme, quindi il passo di formattazione è cruciale:
- Mapping Action: Mappa i campi base in arrivo da Akeneo sui campi richiesti da PrestaShop (ad esempio codice riferimento, ID categoria principale).
- Data Formatting Action: Utilizza l'ambiente C# di Flowlyze per rendere i dati in ingresso pienamente compatibili con l'API PrestaShop. Usa lo script C# per pulire e preparare strutture dati nidificate come i campi multilingua o le regole fiscali.
// 1. MAPPATURA CATEGORIE
System.Collections.Generic.Dictionary<string, string> categoryMap = new System.Collections.Generic.Dictionary<string, string>(System.StringComparer.OrdinalIgnoreCase)
{
{ "3d_printers", "10" },
{ "robotics", "11" },
{ "drones", "12" },
{ "industrial_tools", "13" }
};
var reference = Data.GetString("reference") ?? "DEFAULT-REF";
var nameValue = Data.GetString("name") ?? "Default Product Name";
var weight = Data.GetString("weight") ?? "0.0000";
var price = Data.GetString("price") ?? "0.00";
string idCategory = "2";
var categoryList = Data.GetListString("categories");
if (categoryList != null && categoryList.Count > 0)
{
foreach (var rawCategory in categoryList)
{
if (string.IsNullOrWhiteSpace(rawCategory)) continue;
foreach (var cat in categoryMap)
{
if (rawCategory.IndexOf(cat.Key, System.StringComparison.OrdinalIgnoreCase) >= 0)
{
idCategory = cat.Value;
break;
}
}
if (idCategory != "2") break;
}
}
var activeVal = Data.Get("active");
bool isActive = activeVal != null && System.Convert.ToBoolean(activeVal);
string activeStr = isActive ? "1" : "0";
string linkRewrite = nameValue.ToLower()
.Replace(" ", "-").Replace("ş", "s").Replace("ı", "i").Replace("ğ", "g")
.Replace("ö", "o").Replace("ç", "c").Replace("ü", "u").Replace("---", "-").Replace("--", "-");
// 2. CREAZIONE DEL TEMPLATE XML PRESTASHOP
string xmlOutput = $@"
<prestashop xmlns:xlink=""http://www.w3.org/1999/xlink"">
<product>
<id_category_default><![CDATA[{idCategory}]]></id_category_default>
<reference><![CDATA[{reference}]]></reference>
<price><![CDATA[{price}]]></price>
<weight><![CDATA[{weight}]]></weight>
<active><![CDATA[{activeStr}]]></active>
<state><![CDATA[1]]></state>
<name>
<language id=""1""><![CDATA[{nameValue}]]></language>
<language id=""2""><![CDATA[{nameValue}]]></language>
</name>
<link_rewrite>
<language id=""1""><![CDATA[{linkRewrite}]]></language>
<language id=""2""><![CDATA[{linkRewrite}]]></language>
</link_rewrite>
</product>
</prestashop>";
// 3. INVIO DEL PRODOTTO A PRESTASHOP TRAMITE HTTP REQUEST
var request = new CustomHttpRequest
{
Url = "{{prestashopURL}}/api/products",
Method = "POST",
Headers = new System.Collections.Generic.Dictionary<string, string>
{
{ "Content-Type", "application/xml" }
},
Body = new System.Text.StringBuilder(xmlOutput)
};
string apiKey = "{{Your_apiKey}}";
var authValue = System.Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes($"{apiKey}:"));
request.Headers.Add("Authorization", "Basic " + authValue);
var response = Http.Execute(request);
if (response.Status >= 200 && response.Status < 300)
{
// 4. ESTRAZIONE DELL'ID PRODOTTO
string productId = "";
int startIdx = response.Body.IndexOf("<id><![CDATA[");
if (startIdx != -1)
{
startIdx += 13;
int endIdx = response.Body.IndexOf("]]></id>", startIdx);
if (endIdx != -1)
{
productId = response.Body.Substring(startIdx, endIdx - startIdx);
}
}
// 5. INVIO MASSIVO DEI CODICI IMMAGINE AL PHP PROXY (passo di accelerazione)
var imagesRaw = Data.GetListString("images");
if (!string.IsNullOrEmpty(productId) && imagesRaw != null && imagesRaw.Count > 0)
{
var validAssets = new System.Collections.Generic.List<string>();
foreach (var code in imagesRaw)
{
if (!string.IsNullOrWhiteSpace(code)) validAssets.Add(code);
}
if (validAssets.Count > 0)
{
string assetJson = "[\"" + string.Join("\",\"", validAssets) + "\"]";
string proxyUrl = "{{prestashopURL}}/image_proxy.php";
string jsonPayload = $"{{\"product_id\": \"{productId}\", \"assets\": {assetJson}}}";
var proxyRequest = new CustomHttpRequest
{
Url = proxyUrl,
Method = "POST",
Headers = new System.Collections.Generic.Dictionary<string, string>
{
{ "Content-Type", "application/json" }
},
Body = new System.Text.StringBuilder(jsonPayload)
};
var proxyResponse = Http.Execute(proxyRequest);
Logger.Log($"Operazione immagini via proxy - HTTP {proxyResponse.Status}: {proxyResponse.Body}");
}
}
return Return.Skip($"Operazione riuscita (HTTP {response.Status}): prodotto creato, elaborazione immagini eseguita lato proxy.");
}
else
{
return Return.Error($"Errore PrestaShop (HTTP {response.Status}): {response.Body}");
}
Passo 4.3: Configurare l'action su PrestaShop
Poiché non esiste un nodo nativo, è necessario configurare manualmente l'operazione con un nodo HTTP Request:
- Autenticazione (Auth Config): Aggiungi un HTTP Request come nodo di destinazione. Imposta
Auth TypesuBasic Auth. Incolla la Webservice Key ottenuta da PrestaShop nel campoUsername. Configura il campo password in base ai requisiti di sicurezza. - Impostazioni di base (Settings): Inserisci l'URL di PrestaShop (
{{prestashopURL}}) nel campoBase URL. - Endpoint e metodo: Imposta il
Resource Pathsu/api/productsper la risorsa specifica. Poiché stiamo creando un nuovo prodotto, selezionaPOSTcomeMethod. - Comportamento della richiesta: Seleziona la casella
Send one request per messageper garantire che ogni prodotto venga elaborato singolarmente e senza errori. - Upload immagini (Proxy): Una volta creato il prodotto con successo, estrai il nuovo ID prodotto con C# e invialo insieme ai riferimenti delle immagini all'endpoint
image_proxy.php. Questo file proxy trasferisce le immagini a PrestaShop comemultipart/form-dataall'interno del server.
<?php
$data = json_decode(file_get_contents('php://input'), true);
if (!isset($data['assets']) || !isset($data['product_id'])) {
http_response_code(400);
die(json_encode(["status" => "error", "message" => "Parametri mancanti"]));
}
$productId = $data['product_id'];
$assets = $data['assets'];
$prestaApiKey = '{{Your_apiKey}}';
$akeneoDomain = "{{Your_akeneoDomain}}";
$clientId = "{{Your_clientId}}";
$clientSecret = "{{Your_clientSecret}}";
$username = "{{Your_username}}";
$password = "{{Your_password}}";
// Ottieni il token Akeneo
$chToken = curl_init("$akeneoDomain/api/oauth/v1/token");
curl_setopt($chToken, CURLOPT_POST, true);
curl_setopt($chToken, CURLOPT_POSTFIELDS, http_build_query([
'grant_type' => 'password',
'username' => $username,
'password' => $password,
'client_id' => $clientId,
'client_secret' => $clientSecret
]));
curl_setopt($chToken, CURLOPT_RETURNTRANSFER, true);
$tokenRes = curl_exec($chToken);
curl_close($chToken);
$tokenData = json_decode($tokenRes, true);
$token = $tokenData['access_token'] ?? null;
if (!$token) {
http_response_code(500);
die(json_encode(["status" => "error", "message" => "Impossibile ottenere il token Akeneo.", "response" => $tokenRes]));
}
$results = [];
$tempPath = tempnam(sys_get_temp_dir(), 'img') . '.jpg';
foreach ($assets as $assetCode) {
if (empty($assetCode)) continue;
$chAsset = curl_init("$akeneoDomain/api/rest/v1/asset-families/imported_assets/assets/$assetCode");
curl_setopt($chAsset, CURLOPT_HTTPHEADER, ["Authorization: Bearer $token", "Accept: application/json"]);
curl_setopt($chAsset, CURLOPT_RETURNTRANSFER, true);
$assetRes = curl_exec($chAsset);
$assetStatus = curl_getinfo($chAsset, CURLINFO_HTTP_CODE);
curl_close($chAsset);
$assetData = json_decode($assetRes, true);
$mediaLink = $assetData['values']['media_link'][0]['data'] ?? null;
if (!$mediaLink) {
$results[] = "$assetCode saltato: link non recuperato da Akeneo (HTTP $assetStatus).";
continue;
}
$chImg = curl_init($mediaLink);
$fp = fopen($tempPath, 'wb');
curl_setopt($chImg, CURLOPT_FILE, $fp);
curl_setopt($chImg, CURLOPT_HEADER, 0);
curl_setopt($chImg, CURLOPT_HTTPHEADER, ["Authorization: Bearer $token"]);
curl_exec($chImg);
curl_close($chImg);
if (filesize($tempPath) > 0) {
// Carica su PrestaShop (URL e autenticazione aggiornati)
$uploadUrl = "{{prestashopURL}}/api/images/products/$productId?ws_key=$prestaApiKey";
$chUpload = curl_init($uploadUrl);
curl_setopt($chUpload, CURLOPT_POST, true);
$cFile = curl_file_create($tempPath, 'image/jpeg', 'image.jpg');
curl_setopt($chUpload, CURLOPT_POSTFIELDS, ['image' => $cFile]);
curl_setopt($chUpload, CURLOPT_RETURNTRANSFER, true);
curl_setopt($chUpload, CURLOPT_FOLLOWLOCATION, true);
$upRes = curl_exec($chUpload);
$upStatus = curl_getinfo($chUpload, CURLINFO_HTTP_CODE);
curl_close($chUpload);
if ($upStatus >= 200 && $upStatus < 300) {
$results[] = "$assetCode aggiunto.";
} else {
$results[] = "$assetCode errore: PrestaShop ha restituito HTTP $upStatus. Dettaglio: $upRes";
}
} else {
$results[] = "$assetCode errore: il file è stato scaricato a 0 KB.";
}
}
if (file_exists($tempPath)) unlink($tempPath);
echo json_encode(["status" => "success", "details" => $results]);
?>
5. Test e pubblicazione
Per verificare il flusso HTTP personalizzato:
- Avvia un'esecuzione di test in Flowlyze usando il payload di un singolo prodotto da Akeneo.
- Controlla i log della Execution History di Flowlyze per assicurarti che i passi di Mapping, Formatting e HTTP POST siano stati completati senza errori.
- Se i log indicano un esito positivo, attiva il flusso in produzione. Una volta che il flusso è live, accedi al pannello di amministrazione di PrestaShop e verifica manualmente che i prodotti siano stati aggiunti con la categoria, lo SKU e i prezzi corretti.
6. Gestione errori e troubleshooting
- Problemi di connessione: Verifica che il
Base URLconfigurato in Flowlyze corrisponda all'URL corretto di PrestaShop ({{prestashopURL}}). - Errori di autenticazione (401 Unauthorized): Verifica che la Webservice Key in Basic Auth abbia i permessi necessari per l'operazione
POST(scrittura) nel pannello PrestaShop. - Incongruenze di formato (400 Bad Request): L'HTTP Request dialoga direttamente con l'API e non tollera errori strutturali. Controlla l'output generato dal codice C# per assicurarti che i campi obbligatori di PrestaShop (ad esempio gli ID lingua mancanti) non siano omessi.
7. Conclusioni e prossimi passi
Senza bisogno di un nodo di integrazione nativo, hai costruito un ponte dati completamente automatizzato tra Akeneo e PrestaShop usando le capacità HTTP Request di Flowlyze. Come passo successivo, puoi arricchire la pipeline senza codice aggiuntivo integrando uno step di notifica (ad esempio Slack o Email) per ricevere avvisi automatici ogni volta che una sincronizzazione prodotto viene completata o se si verifica un errore.