facebook

Blog

Resta aggiornato

Vediamo insieme come integrare le schede prodotti del nostro e-commerce con le informazioni provenienti da Amazon
Quando Amazon ti fa il servizio…
martedì 19 Marzo 2019

Durante un’attività di consulenza per un cliente, che ha commissionato lo sviluppo di un e-commerce in ambito tecnologico, ci siamo trovati ad affrontare la scarsità di informazioni tecniche e commerciali messe a disposizione dal fornitore dei prodotti, per cui, in fase di presentazione, avremmo avuto un sito spoglio e schede prodotto piuttosto limitate.

Una delle soluzioni migliori trovate per aggiungere o completare le informazioni sui prodotti è stato l’utilizzo delle Amazon Product Advertising API (ex Amazon Web Service AWS), una serie di servizi che Amazon mette a disposizione per la vendita dei propri prodotti su altri store.

Dopo una veloce e gratuita registrazione come Amazon Associate, possiamo iscriverci al programma di Amazon Product Advertising API, ottenendo le credenziali per l’utilizzo dei servizi.

Consultando la documentazione, è possibile vedere che sono disponibili i seguenti servizi:

  • ItemSearch: ricerca prodotti su Amazon;
  • BrowseNodeLookup: restituisce la gerarchia delle categorie a partire da un prodotto/categoria;
  • ItemLookup: restituisce una lista di attributi per l’oggetto specificato;
  • SimilarityLookup: restituisce gli oggetti simili all’oggetto specificato.

Inoltre, nell’invocazione di questi, è possibile aggiungere ulteriori parametri che si vogliono ottenere nella risposta. Ad esempio, tra i parametri disponibili ci sono: Accessories (oggetti acquistati insieme); EditoralReview (descrizione del prodotto); Images (set delle immagini in varie dimensioni); ItemAttributes (caratteristiche peculiari); OfferListing (elenco delle offerte dei vari venditori); RelatedItems (oggetti collegati); Reviews (recensioni).

Tra tutti i servizi, ItemSearch risulta quello più completo a soddisfare le nostre esigenze e, probabilmente, anche quello che ci consente di fare meno invocazioni, dal momento che riceve in input l’EAN (codice a barre di 13 cifre) e restituisce le informazioni di cui abbiamo necessità, come nella response di esempio di seguito:

<Item>
    <ASIN>B00TZL96BY</ASIN>
    <ParentASIN>B012E5CKMY</ParentASIN>
    <DetailPageURL>https://www.amazon.com/Lacoste-Womens-Sleeve-Stretch-Varsity/dp/B00TZL96BY%3Fpsc%3D1%26SubscriptionId%3D[AWS Access Key ID]%26tag%3D[Associate ID]%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3DB00TZL96BY</DetailPageURL>
    <ItemLinks>
        <ItemLink>
            <Description>Technical Details</Description>
            <URL>https://www.amazon.com/Lacoste-Womens-Sleeve-Stretch-Varsity/dp/tech-data/B00TZL96BY%3FSubscriptionId%3D[AWS Access Key ID]%26tag%3D[Associate ID]%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB00TZL96BY</URL>
        </ItemLink>
        <ItemLink>
            <Description>Add To Baby Registry</Description>
            <URL>https://www.amazon.com/gp/registry/baby/add-item.html%3Fasin.0%3DB00TZL96BY%26SubscriptionId%3D[AWS Access Key ID]%26tag%3D[Associate ID]%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB00TZL96BY</URL>
        </ItemLink>
        <ItemLink>
            <Description>Add To Wedding Registry</Description>
            <URL>https://www.amazon.com/gp/registry/wedding/add-item.html%3Fasin.0%3DB00TZL96BY%26SubscriptionId%3D[AWS Access Key ID]%26tag%3D[Associate ID]%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB00TZL96BY</URL>
        </ItemLink>
        ...
    </ItemLinks>
    <ItemAttributes>
        <Manufacturer>Lacoste Womens Apparel</Manufacturer>
        <ProductGroup>Apparel</ProductGroup>
        <Title>Lacoste Women's Long Sleeve Stretch Pique Slim Fit Polo Shirt, Varsity Blue, 40</Title>
    </ItemAttributes>
</Item>

Il limite di utilizzo gratuito del servizio di è di 25.000 chiamate per ora, per cui risulta particolarmente utile anche nel caso di grossi e-commerce.

Prima di utilizzare i servizi, naturalmente, è necessario autenticarsi, utilizzando il client id e il secret ricevuto in fase di registrazione.

Per l’implementazione in ambiente .NET abbiamo utilizzato una classe Java trovata tra gli esempi forniti da Amazon, SignedRequestsHelper, e l’abbiamo riscritta in C#. Questa classe ci fornisce l’accesso e l’utilizzo dei servizi di Amazon.

La nostra scelta, anche per ovviare ai limiti di utilizzo del servizio e gestire al meglio gli errori, è stata di procedere secondo due step differenti:

  1. Salvare le informazioni dei prodotti su un DB di Swap;
  2. Allineare i prodotti Amazon con quelli dell’e-commerce.

Così facendo, nella seconda fase potremmo decidere cosa importare, definire le similarità tra i prodotti, ed eventualmente ripristinare informazioni modificate per errore.

Implementiamo quindi un servizio di importazione, AmazonSwapImportManager, in cui, nel costruttore, istanziamo un oggetto SignedRequestHelper passandogli i parametri ottenuti in fase di registrazione:

SignedRequestHelper helper;
 
public AmazonSwapImportManager() {
    try {
        helper = new SignedRequestHelper(ACCESS_KEY_ID, SECRET_KEY, ENDPOINT);
    } catch (Exception e) {
        //logga l’eccezione;
    }
}

Per ottenere le informazioni relative ai prodotti, invece, creiamo un metodo GetInfoFromAmazonServices:

private String GetInfoFromAmazonServices(String ean) {
    try {
        //ottieni la url del prodotto richiesto tramite il suo EAN
        String requestUrl = GetProductUrlRequestByEan(ean);
        //ottieni l'oggetto XML dalla url del prodotto
        String xmlObject = GetXmlObjectFromUrl(requestUrl).Result;
        //trasforma in JSON l'oggetto XML ricevuto precedentemente.
        return GetJsonFromXml(xmlObject);
    } catch (Exception e) {
        //logga l’eccezione;
    }
}

che si basa su altri metodi di utility:

private static String GetJsonFromXml(string xmlObject) {
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(xmlObject);
    var node = doc.DocumentElement.GetElementsByTagName("Item");
    if (node.Item(0) == null) return "none";
    string json = JsonConvert.SerializeXmlNode(node.Item(0));
    return json;
}
 
private String GetProductUrlRequestByEan(String ean) {
    IDictionary <string, string> requestParams = new Dictionary <string, String> ();
 
    requestParams.Add("Service", "AWSECommerceService");
    requestParams.Add("Operation", "ItemSearch");
    requestParams.Add("AWSAccessKeyId", ACCESS_KEY);
    requestParams.Add("AssociateTag", TAG);
    requestParams.Add("Keywords", EAN);
    requestParams.Add("IdType", "EAN");
    requestParams.Add("ResponseGroup", "Accessories,AlternateVersions,BrowseNodes,EditorialReview,Images,ItemAttributes,ItemIds,OfferFull,OfferListings,Offers,OfferSummary,PromotionSummary,RelatedItems,Reviews,SalesRank,Similarities");
    requestParams.Add("SearchIndex", "All");
    requestParams.Add("RelationshipType", "AuthorityTitle");
 
    return helper.Sign(requestParams);
}
 
private async Task <String> GetXmlObjectFromUrl(String requestUrl) {
    return await requestUrl.GetStringAsync();
}

Il metodo GetInfoFromAmazonServices viene utilizzato all’interno di un metodo ImportProductInfo, che cicla su tutti i prodotti dello store e tenta di ottenere le informazioni di cui necessitiamo:

private void ImportProductsInfo(IList <Product> products) {
 /* altre operazioni di utilità */
 
    //cicla sui prodotti
    foreach(var product in products) {
        String productInfo = GetInfoFromAmazonServices(product.Ean);
        //salva su un DB di swap le informazioni trovate sul prodotto
        StoreItemToAmazonSwapDB(product.Ean, productInfo);
    }
    /* altre operazioni di riepilogo */
}

L’oggetto salvato sul DB di Swap è di tipo SwapItem, così definito:

public class SwapItem
{
    public int Id { get; set; }
    public string Ean { get; set; }
    public string Content { get; set; }
}

Una volta salvati i dati relativi ai prodotti su un DB di swap, procediamo all’allineamento col nostro DB di produzione. Creiamo una classe AmazonSwapSyncer, che contiene il metodo Sync, così definito:

public void Sync()
{
    SynchronizeProductsInfo(GetItemsFromAmazonSwapDB());
}

che, a sua volta, si basa su SynchronizeProductsInfo:

private void SynchronizeProductsInfo(IList <SwapItem> swapItems) {
 /* altre operazioni di utilità */
 
    foreach(var swapItem in swapItems) {
    //ricerca sul nostro DB se ci sono occorrenze del prodotto con l'EAN specificato
        var currentProduct = dbContext.Products.FirstOrDefault(p => p.Ean == swapItem.Ean);
        if (currentProduct != null) {
            try {
            //deserializza la stringa in un oggetto AmazonItem
                var amazonItem = JsonConvert.DeserializeObject <AmazonItem> (swapItem.Content);
                //ottiene la descrizione
                currentProduct.Description = amazonItem.Item.EditorialReviews.EditorialReview.Content;
                //ottiene le caratteristiche techiche del prodotto
                currentProduct.Features = String.Join(" * ", amazonItem.Item.ItemAttributes.Feature);
                //ottiene la url dell'immagine di grandi dimensioni
                currentProduct.ImageUrl = amazonItem.Item.LargeImage.Url;
                /* ottieni altre informazioni */
            } catch (Exception e) {
                //logga l’eccezione;
            }
        }
    }
    //salva le informazioni ottenute
    dbContext.SubmitChanges();
}

La classe AmazonItem, con le varie classi aggregate, è così definita a seconda delle nostre necessità:

public class AmazonItem
{
    public Item Item { get; set; }      
}
 
public class Item
{
    public String ASIN { get; set; }
    public long SalesRank { get; set; }
    public ItemAttributes ItemAttributes { get; set; }
    public EditorialReviews EditorialReviews { get; set; }
    public AmazonImage LargeImage { get; set; }
}
 
public class ItemAttributes
{
    public String Binding { get; set; }
    public String Color { get; set; }
    public String[] Feature { get; set; }
}
 
public class EditorialReviews
{
    public EditorialReview EditorialReview { get; set; }
}
 
public class EditorialReview
{
    public String Content { get; set; }
}
 
public class AmazonImage
{
    public String Url { get; set; }
    public int Height { get; set; }
    public int Width { get; set; }
}

Per cui, alla fine del ciclo di sincronizzazione, abbiamo informazioni dei prodotti aggiornate grazie ai contenuti disponibili su Amazon.

Il codice citato in questo articolo è disponibile su GitHub al seguente link.