Torna al Blog
Case Study

n8n per l'e-commerce: come automatizzare ordini, inventario e spedizioni

Case study di un'azienda di e-commerce che usa n8n per automatizzare l'intera catena operativa: sincronizzazione marketplace, gestione inventario e notifiche clienti in tempo reale.

Marco Ferri- Automation Architect28 marzo 202611 min read
n8n per l'e-commerce: come automatizzare ordini, inventario e spedizioni

Il contesto: un'azienda di e-commerce in crescita

TechStyle Italia è un'azienda milanese specializzata nella vendita online di accessori tech. Fondata nel 2019, ha raggiunto nel 2025 un fatturato di 2,3 milioni di euro vendendo su tre canali: il proprio sito Shopify, Amazon.it e eBay.

Con una media di 150 ordini al giorno distribuiti su tre marketplace, il team di 8 persone faticava a mantenere la sincronizzazione tra i sistemi. I problemi principali erano:

  • Inventario disallineato: vendite su Amazon non aggiornavano le giacenze su Shopify, causando ordini per prodotti esauriti
  • Notifiche ai clienti in ritardo: gli aggiornamenti di spedizione venivano inseriti manualmente, spesso con 12-24 ore di ritardo
  • Report manuali: ogni venerdì, un dipendente dedicava 3 ore alla preparazione del report settimanale
  • Dati vendite frammentati: le informazioni su Amazon, eBay e Shopify vivevano in sistemi separati

La decisione è stata quella di implementare n8n come hub centrale di automazione, collegando tutti i marketplace, il gestionale interno e i canali di comunicazione con i clienti.

Architettura del sistema

Prima di entrare nei dettagli dei singoli workflow, vediamo l'architettura complessiva:

MARKETPLACE (Shopify, Amazon, eBay)
       |
       v
    n8n HUB (orchestrazione workflow)
    /    |    \
   v     v     v
ERP   DATABASE  SERVIZI ESTERNI
(inventario)  (report)  (email, SMS, corrieri)

n8n funziona come "collante" tra sistemi che altrimenti non comunicherebbero tra loro. Ogni marketplace invia webhook a n8n per gli eventi critici (nuovo ordine, cambio stato, reso), e n8n si occupa di propagare l'informazione a tutti gli altri sistemi.

Stack tecnologico

  • Marketplace: Shopify (sito proprio), Amazon Seller Central, eBay API
  • ERP: sistema custom su PostgreSQL (gestione inventario e ordini)
  • Corrieri: GLS, BRT, DHL Express (via API)
  • Comunicazioni: email (Postmark), SMS (Twilio), WhatsApp Business
  • Report: Google Sheets + Google Data Studio
  • Automazione: n8n self-hosted su VPS (Ubuntu, 4 vCPU, 8 GB RAM)

Workflow 1: sincronizzazione ordini centralizzata

Il primo problema da risolvere era la raccolta degli ordini da tutti i marketplace in un formato unificato.

Normalizzazione degli ordini

Ogni marketplace ha un formato diverso per gli ordini. Il nodo Code in n8n si occupa della normalizzazione:

const item = $input.first().json;
const source = item.source; // 'shopify', 'amazon', 'ebay'

function normalizeShopify(order) {
  return {
    source: 'shopify',
    external_id: order.id.toString(),
    customer_name: `${order.shipping_address.first_name} ${order.shipping_address.last_name}`,
    customer_email: order.email,
    customer_phone: order.shipping_address.phone || '',
    shipping_address: {
      street: order.shipping_address.address1,
      city: order.shipping_address.city,
      province: order.shipping_address.province,
      zip: order.shipping_address.zip,
      country: order.shipping_address.country_code
    },
    items: order.line_items.map(li => ({
      sku: li.sku,
      name: li.title,
      quantity: li.quantity,
      unit_price: parseFloat(li.price),
      total: parseFloat(li.price) * li.quantity
    })),
    total: parseFloat(order.total_price),
    currency: order.currency || 'EUR',
    shipping_method: order.shipping_lines?.[0]?.title || 'Standard',
    notes: order.note || '',
    created_at: order.created_at
  };
}

function normalizeAmazon(order) {
  return {
    source: 'amazon',
    external_id: order.AmazonOrderId,
    customer_name: order.BuyerInfo?.BuyerName || 'N/A',
    customer_email: order.BuyerInfo?.BuyerEmail || '',
    customer_phone: order.BuyerInfo?.BuyerPhoneNumber || '',
    shipping_address: {
      street: order.ShippingAddress?.AddressLine1 || '',
      city: order.ShippingAddress?.City || '',
      province: order.ShippingAddress?.StateOrRegion || '',
      zip: order.ShippingAddress?.PostalCode || '',
      country: order.ShippingAddress?.CountryCode || 'IT'
    },
    items: (order.OrderItems || []).map(oi => ({
      sku: oi.SellerSKU,
      name: oi.Title,
      quantity: parseInt(oi.QuantityOrdered),
      unit_price: parseFloat(oi.ItemPrice?.Amount || 0),
      total: parseFloat(oi.ItemPrice?.Amount || 0) * parseInt(oi.QuantityOrdered)
    })),
    total: parseFloat(order.OrderTotal?.Amount || 0),
    currency: order.OrderTotal?.CurrencyCode || 'EUR',
    shipping_method: order.ShippingServiceLevel || 'Standard',
    notes: '',
    created_at: order.PurchaseDate || order.LastUpdateDate
  };
}

function normalizeEbay(order) {
  return {
    source: 'ebay',
    external_id: order.orderId,
    customer_name: order.buyer?.username || 'N/A',
    customer_email: order.contact?.email || '',
    customer_phone: order.contact?.phone || '',
    shipping_address: {
      street: order.shippingDetails?.address?.addressLine1 || '',
      city: order.shippingDetails?.address?.city || '',
      province: order.shippingDetails?.address?.stateOrProvince || '',
      zip: order.shippingDetails?.address?.postalCode || '',
      country: order.shippingDetails?.address?.countryCode || 'IT'
    },
    items: (order.lineItems || []).map(li => ({
      sku: li.sku || '',
      name: li.title,
      quantity: parseInt(li.quantity),
      unit_price: parseFloat(li.price?.value || 0),
      total: parseFloat(li.price?.value || 0) * parseInt(li.quantity)
    })),
    total: parseFloat(order.total?.value || 0),
    currency: order.total?.currency || 'EUR',
    shipping_method: order.shippingDetails?.service?.shippingService || 'Standard',
    notes: '',
    created_at: order.creationDate
  };
}

const normalizers = {
  shopify: normalizeShopify,
  amazon: normalizeAmazon,
  ebay: normalizeEbay
};

const normalized = normalizers[source]
  ? normalizers[source](item)
  : { error: `Fonte non supportata: ${source}` };

return [{ json: normalized }];

Salvataggio nel database centralizzato

Dopo la normalizzazione, l'ordine viene salvato nel database PostgreSQL:

{
  "parameters": {
    "operation": "executeQuery",
    "query": "INSERT INTO orders (source, external_id, customer_name, customer_email, customer_phone, shipping_street, shipping_city, shipping_province, shipping_zip, shipping_country, total, currency, shipping_method, notes, status, created_at) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, 'pending', $15) ON CONFLICT (source, external_id) DO NOTHING RETURNING id",
    "queryParameters": {
      "parameters": [
        { "name": "source", "type": "string", "value": "={{ $json.source }}" },
        { "name": "external_id", "type": "string", "value": "={{ $json.external_id }}" },
        { "name": "customer_name", "type": "string", "value": "={{ $json.customer_name }}" },
        { "name": "customer_email", "type": "string", "value": "={{ $json.customer_email }}" },
        { "name": "customer_phone", "type": "string", "value": "={{ $json.customer_phone }}" },
        { "name": "shipping_street", "type": "string", "value": "={{ $json.shipping_address.street }}" },
        { "name": "shipping_city", "type": "string", "value": "={{ $json.shipping_address.city }}" },
        { "name": "shipping_province", "type": "string", "value": "={{ $json.shipping_address.province }}" },
        { "name": "shipping_zip", "type": "string", "value": "={{ $json.shipping_address.zip }}" },
        { "name": "shipping_country", "type": "string", "value": "={{ $json.shipping_address.country }}" },
        { "name": "total", "type": "number", "value": "={{ $json.total }}" },
        { "name": "currency", "type": "string", "value": "={{ $json.currency }}" },
        { "name": "shipping_method", "type": "string", "value": "={{ $json.shipping_method }}" },
        { "name": "notes", "type": "string", "value": "={{ $json.notes }}" },
        { "name": "created_at", "type": "string", "value": "={{ $json.created_at }}" }
      ]
    }
  },
  "type": "n8n-nodes-base.postgres",
  "name": "Salva Ordine Centralizzato"
}

Workflow 2: gestione inventario in tempo reale

L'inventario è il cuore dell'e-commerce. Una giacenza sbagliata significa ordini cancellati, clienti insoddisfatti e soldi persi.

Logica di aggiornamento inventario

Ogni volta che un ordine viene confermato, n8n:

  1. Legge gli SKU dell'ordine
  2. Sottrae le quantità dal database inventario
  3. Se la giacenza scende sotto la soglia minima, invia un alert
  4. Aggiorna le giacenze su tutti i marketplace
Webhook (nuovo ordine confermato)
  -> Code (estrae lista SKU + quantità)
  -> PostgreSQL (aggiorna giacenze con transazione)
  -> IF (giacenza < soglia minima per qualche SKU)
    -> true: Slack (alert riordino urgente)
  -> Loop Over Items (per ogni SKU modificato)
    -> Shopify API (aggiorna giacenza)
    -> Amazon API (aggiorna giacenza)
    -> eBay API (aggiorna giacenza)

Aggiornamento giacenza su Shopify

{
  "parameters": {
    "method": "POST",
    "url": "=https://<SHOP>.myshopify.com/admin/api/2024-01/inventory_levels/set.json",
    "authentication": "genericCredentialType",
    "genericAuthType": "httpHeaderAuth",
    "sendHeaders": true,
    "headerParameters": {
      "parameters": [
        {
          "name": "X-Shopify-Access-Token",
          "value": "<SHOPIFY_ACCESS_TOKEN>"
        }
      ]
    },
    "sendBody": true,
    "contentType": "json",
    "body": "={{ JSON.stringify({ location_id: '<LOCATION_ID>', inventory_item_id: $json.shopify_inventory_id, available: $json.nuova_giacenza }) }}"
  },
  "type": "n8n-nodes-base.httpRequest",
  "name": "Aggiorna Giacenza Shopify"
}

Alert di riordino intelligente

Il nodo Code calcola le soglie di riordino basandosi sulla velocità di vendita:

const items = $input.all();
const alerts = [];

items.forEach(item => {
  const giacenza = item.json.nuova_giacenza;
  const vendite_7gg = item.json.vendite_settimanali || 0;
  const vendite_giorno = vendite_7gg / 7;
  const giorni_copertura = vendite_giorno > 0 ? Math.floor(giacenza / vendite_giorno) : 999;

  // Soglia: almeno 14 giorni di copertura
  const soglia_minima = Math.ceil(vendite_giorno * 14);

  if (giacenza <= soglia_minima || giacenza <= 5) {
    alerts.push({
      json: {
        sku: item.json.sku,
        nome: item.json.nome_prodotto,
        giacenza_attuale: giacenza,
        vendite_giorno: vendite_giorno.toFixed(1),
        giorni_copertura: giorni_copertura,
        suggerimento_riordino: Math.max(soglia_minima * 2, 20),
        urgenza: giorni_copertura <= 3 ? 'CRITICA' : giorni_copertura <= 7 ? 'ALTA' : 'MEDIA'
      }
    });
  }
});

return alerts.length > 0 ? alerts : [{ json: { nessun_alert: true } }];

Workflow 3: notifiche clienti multicanale

TechStyle Italia ha implementato un sistema di notifica multicanale: email, SMS e WhatsApp, a seconda della preferenza del cliente e del tipo di evento.

Matrice di notifica

EventoEmailSMSWhatsApp
Conferma ordineNo
Ordine in lavorazioneNoNo
Ordine spedito (con tracking)
Consegna avvenutaNoNo
Richiesta recensioneNo
Reso autorizzatoNo

Invio notifica spedizione con tracking

Quando il corriere conferma il ritiro, n8n invia la notifica su tutti i canali configurati:

// Nodo Code - Genera notifica spedizione
const item = $input.first().json;

const trackingUrl = `https://www.${item.corriere.toLowerCase()}.it/track?id=${item.tracking_number}`;

const emailHtml = `
<h2>Il tuo ordine #${item.order_id} è stato spedito!</h2>
<p>Ciao ${item.customer_name},</p>
<p>Il tuo ordine è stato affidato al corriere <strong>${item.corriere}</strong>.</p>
<p><strong>Numero di spedizione:</strong> ${item.tracking_number}</p>
<p><a href="${trackingUrl}" style="background:#1a73e8;color:white;padding:12px 24px;text-decoration:none;border-radius:6px;display:inline-block;">Traccia la spedizione</a></p>
<p>Tempo stimato di consegna: <strong>${item.stimated_delivery}</strong></p>
`;

const smsText = `TechStyle: Il tuo ordine #${item.order_id} è stato spedito con ${item.corriere}. Tracking: ${item.tracking_number}. Traccia: ${trackingUrl}`;

return [{
  json: {
    ...item,
    email_subject: `Il tuo ordine #${item.order_id} è in viaggio!`,
    email_html: emailHtml,
    sms_text: smsText,
    whatsapp_template: 'ordine_spedito',
    whatsapp_params: [
      item.customer_name,
      item.order_id,
      item.corriere,
      item.tracking_number,
      trackingUrl
    ]
  }
}];

Workflow 4: reportistica automatica

Prima di n8n, il report settimanale richiedeva 3 ore di lavoro manuale. Ora viene generato automaticamente ogni lunedì mattina alle 7:00.

Generazione del report

Schedule Trigger (lunedì alle 7:00)
  -> PostgreSQL (query vendite settimana precedente)
  -> Code (calcola KPI: fatturato, ordini, ticket medio, top prodotti, top canali)
  -> Google Sheets (scrivi report nel foglio condiviso)
  -> Code (genera riassunto Markdown)
  -> Gmail (invia report al management)
  -> Slack (pubblica riepilogo nel canale #vendite)

Query KPI settimanali

SELECT
  source AS canale,
  COUNT(*) AS totale_ordini,
  ROUND(SUM(total), 2) AS fatturato,
  ROUND(AVG(total), 2) AS ticket_medio,
  COUNT(DISTINCT customer_email) AS clienti_unici,
  COUNT(CASE WHEN status = 'returned' THEN 1 END) AS resi
FROM orders
WHERE created_at >= DATE_TRUNC('week', CURRENT_DATE - INTERVAL '1 week')
  AND created_at < DATE_TRUNC('week', CURRENT_DATE)
GROUP BY source
ORDER BY fatturato DESC;

Report prodotti più venduti

SELECT
  oi.sku,
  oi.nome,
  SUM(oi.quantity) AS quantita_venduta,
  ROUND(SUM(oi.total), 2) AS fatturato_prodotto,
  COUNT(DISTINCT o.id) AS numero_ordini
FROM order_items oi
JOIN orders o ON o.id = oi.order_id
WHERE o.created_at >= DATE_TRUNC('week', CURRENT_DATE - INTERVAL '1 week')
  AND o.created_at < DATE_TRUNC('week', CURRENT_DATE)
GROUP BY oi.sku, oi.nome
ORDER BY fatturato_prodotto DESC
LIMIT 20;

Workflow 5: gestione resi e rimborsi

I resi sono una parte inevitabile dell'e-commerce. Con n8n, il processo è semi-automatizzato:

Webhook (richiesta reso da sito)
  -> IF (entro 14 giorni dalla consegna?)
    -> false: Email (comunica impossibilità di reso)
    -> true: IF (prodotto in elenco non resituibile?)
      -> true: Email (comunica prodotto escluso)
      -> false: Aggiorna stato ordine a "return_requested"
        -> Email cliente (etichetta reso + istruzioni)
        -> Wait (massimo 14 giorni per ricevere il reso)
        -> Webhook (ricezione reso al magazzino)
          -> IF (prodotto integro?)
            -> true: Rimborsa automatico
              -> Aggiorna inventario (+1)
              -> Aggiorna ordine a "refunded"
              -> Email conferma rimborso
            -> false: Email (comunica danni, gestisci caso per caso)

Risultati ottenuti dopo 6 mesi

L'implementazione dei workflow n8n ha prodotto risultati misurabili:

MetricaPrima di n8nDopo n8nMiglioramento
Tempo per elaborazione ordine25 minuti2 minuti-92%
Errori inventario (mese)15-201-2-90%
Ordini per prodotti esauriti8-12 al mese0-1 al mese-95%
Tempo report settimanale3 ore0 (automatico)-100%
Tempo notifica spedizione12-24 ore5 minuti-98%
Costo mensile automazioneN/A~45 EUR (VPS)N/A
Ore risparmiate al meseN/A~60 oreN/A

Lezioni apprese

Errori da evitare

  1. Non sottovalutare la normalizzazione dei dati: ogni marketplace ha formati diversi. Investi tempo nella mappatura fin dall'inizio, altrimenti i dati saranno incoerenti
  2. Gestisci i rate limit: Amazon e eBay hanno limiti di API rigorosi. Implementa sempre backoff esponenziale e code di retry
  3. Log tutto: ogni transazione, ogni errore, ogni aggiornamento inventario. Senza log, il debugging è impossibile
  4. Testa con ordini finti: prima di mettere in produzione, crea ordini di test su ogni marketplace per verificare l'intera catena

Consigli per la scalabilità

  • Monitoraggio: usa il nodo Error Trigger di n8n collegato a Slack per ricevere alert in tempo reale
  • Backup: configura un backup automatico del database PostgreSQL ogni 6 ore
  • Documentazione: mantieni un documento aggiornato con tutti i webhook attivi e le relative credenziali (senza i valori segreti)
  • Versioning dei workflow: usa la funzione di export/import di n8n per versionare i workflow nel repository Git dell'azienda

Conclusione

Il caso di TechStyle Italia dimostra che n8n non è solo uno strumento per piccole automazioni, ma può diventare il sistema nervoso centrale di un'azienda di e-commerce con volumi significativi. La chiave del successo è stata un'implementazione graduale: prima i workflow più critici (inventario e notifiche), poi i report e infine i processi più complessi (resi e rimborsi).

Per le aziende italiane che vendono su più marketplace, n8n rappresenta una soluzione particolarmente vantaggiosa perché elimina la necessità di costose piattaforme di integrazione multi-canale, mantenendo al contempo la flessibilità di adattare le automazioni ai processi specifici del business.

M

Marco Ferri

Automation Architect

Specialista in workflow automation e integrazioni enterprise. Oltre 15 anni di esperienza in architetture IT per PMI italiane.

Articoli correlati