Réaliser un panier 100% JavaScript

Une boutique de e-commerce nécessite la plupart du temps un panier. De nombreuses solutions peuvent répondre à ce besoin. Il est possible de mettre en place un panier côté serveur, d’adjoindre un plugin au framwork ou CMS utilisé pour le site etc. Cependant, nous allons voir comment programmer un panier 100% en JavaScript sans utiliser aucun plugin ni code côté serveur.

La panier côté client présente certains avantages. Il ne nécessite pas le rechargement de la page à chaque ajout d’article et il est indépendant du code utilisé pour bâtir le site. Ainsi, on l’adjoindra sans problème à des CMS comme WordPress ou Joomla sans devoir modifier le code du CMS ou installer de plugin.

Il existe bien entendu des librairies JavaScript open source qui permettent d’avoir un panier clef en main. Néanmoins, développer sa solution soit-même donne l’assurance d’obtenir quelque chose qui correspond à 100% à ses besoins tout en restant léger.

J’ai à la base créé ce panier pour le site Alphapole, spécialisé dans la magnétothérapie et la purification de l’eau. J’ai conçu le site avec WordPress, néanmoins, n’étant pas fan des plugins e-commerce du CMS, j’ai opté pour un panier sur-mesure. Vous pouvez d’ailleurs vous rendre sur le site pour voir ce dernier en action.

Je voulais éviter les rechargements de page, lesquels nuisent à l’expérience utilisateur – spécialement sur mobile où le réseau peut être lent. Une solution client-side basée sur jQuery semblait alors une bonne idée. En outre, les aimants étant lourds, il était impératif de prendre en compte le poids de la marchandise dans les frais de port. Cet article se basera sur des exemples du site d’Alphapole. Ce dernier repose sur Bootstrap, j’enleverai cependant les classes et éléments HTML de présentation pour plus de clarté. Assez discuté, commençons !

Le HTML de base

Le panier se compose de trois parties :

  • Les articles : le code formaté de manière à ce qu’on retrouve les informations liées au produit,
  • L’icône du panier qui indique au survol les articles qu’il contient,
  • La page du panier qui permet de modifier les quantités, de supprimer les articles etc.

Les articles

Il s’agit simplement d’un bouton permettant l’ajout au panier, et d’un champ select qui propose de sélectionner une quantité allant de 1 à 9. L’exemple ci-dessous est extrait des Pastilles Physiomag.

<label for="q">Quantité: </label>
<select id="qt" name="q">
  <option value="1">1</option>
  <option value="2">2</option>
  <option value="3">3</option>
  <option value="4">4</option>
  <option value="5">5</option>
  <option value="6">6</option>
  <option value="7">7</option>
  <option value="8">8</option>
  <option value="9">9</option>
</select><br>
<button type="button" class="add-to-cart" data-id="432" data-name="Pastilles" data-price="29,00" data-weight="97" data-url="/produit/pastilles-anti-douleurs/">Ajouter au panier</button>

Il n’y a rien de particulier à dire sur le select et son label. Le JavaScript viendra simplement récupérer la valeur de input dont l’id est « qt ». Par ailleurs, la sélection de la quantité n’est proposée que sur les fiches produit individuelles et non sur les pages listant plusieurs produits. Si on veut proposer la quantité sur ces dernières, c’est possible moyennant quelques modifications. Attention, il ne doit pas y avoir deux fois le même id sur une page.

Le bouton – qui est le code minimal pour que le panier soit fonctionnel – concentre l’essentiel. Toutes les informations liées à l’article sont stockées dans les attributs data-* :

  • data-id est l’identifiant de l’article. Ainsi, si on ajoute un article déjà présent au panier, on incrémente la quantité au lieu de l’ajouter comme un nouvel article,
  • data-name concerne, vous vous en doutiez, le nom de l’article,
  • data-price contient le prix. J’ai fait le choix d’un formatage à la française car je trouvais plus simple à gérer comme ça pour l’affichage,
  • data-weight contient le poids en grammes du produit, ce poids sera utilisé pour calculer les frais de port,
  • data-url renseigne l’url de la fiche produit.

Le widget panier

Le code de l’icône ou widget du panier est assez simple :

<div class="dropdown">
  <div id="cart">
    <p><span id="in-cart-items-num">0</span> Articles</p>
  </div>
                              
  <ul id="cart-dropdown" hidden>
    <li id="empty-cart-msg"><a>Votre panier est vide</a></li>
    <li class="go-to-cart hidden"><a href="/panier/">Voir le panier</a></li>
  </ul>
</div>

Il s’agit simplement d’une balise p qui indique le nombre d’articles dans le panier. Au survol de celle-ci apparaît une liste ul. La liste possède par défaut un message indiquant que le panier est vide et un lien – en display:hidden grâce à l’attribut HTML5 hidden tant que le panier est vide – permettant d’aller au panier.

Pour faire apparaître la liste des produits ajoutés lors du survol du widget, on peut utiliser du CSS ou du JavaScript. Dans mon infinie bonté, je vous donnes les deux possibilités :

/* CSS */
.dropdown:hover > #cart-dropdown{
  display: block;
}

/* JS */
// comportement du panier au survol pour affichage de son contenu
var timeout;

$('#cart').on({
  mouseenter: function() {
    $('#cart-dropdown').show();
  },
  mouseleave: function() {
    timeout = setTimeout(function() {
      $('#cart-dropdown').hide();
    }, 200);
  }
});

// laisse le contenu ouvert à son survol
// le cache quand la souris sort
$('#cart-dropdown').on({
  mouseenter: function() {
    clearTimeout(timeout);
  },
  mouseleave: function() {
    $('#cart-dropdown').hide();
  }
});

J’aime l’élégance et la simplicité du CSS. La solution JavaScript offre néanmoins beaucoup plus de souplesse. Par exemple, on fixe ici un certain temps entre le moment ou la souris quitte le #cart et où #cart-dropdown disparaît. Ça nous permet de gérer le cas où la liste n’est pas directement accolée au p.

La page panier

La page dédiée au panier présente plus de détails que le widget et ajoute des éléments de contrôle.

<table class="table">
  <thead>
    <tr><th>Article</th><th>Prix</th><th>Quantité</th></tr>
  </thead>
  <tbody id="cart-tablebody">
  </tbody>
</table>
        
<p>Sous total : <span class="subtotal"></span>€</p>

<button id="confirm-command">Passer la commande</button>

Un tableau regroupe les en-têtes de nos différentes colonnes, le corps du tableau et le sous total, lesquels seront peuplés par le JS. Enfin, nous trouvons aussi un bouton permettant de confirmer la commande et de passer à la phase de paiement.

Les fonctions de bases

Nous avons besoin de stocker certaines informations afin que notre panier ne se vide pas à chaque changement de page ou lorsque l’internaute quitte le site puis y revient plus tard. On a le choix entre l’api localStorage et les classiques cookies. Nous allons opter pour les cookies car ils sont mieux supportés que le localStorage. Le plus gros problème de ce dernier survient dans Safari en mode navigation privé : le navigateur le rend tout bonnement indisponible, comme s’il n’était pas supporté. En outre, les cookies permettent de spécifier une date de péremption. Ainsi, quelques jours après sa visite, si un internaute n’a toujours pas passé commande, on peut estimer que son panier n’est plus valable.

Les fonctions permettant de gérer les cookies en JavaScript ne sont pas ultra ergonomiques. Nous allons créer nos propres fonctions pour enregistrer et récupérer facilement des éléments de notre panier.

setCookie

Commençons par créer la fonction qui permet d’enregistrer des données dans un cookie :

function setCookie(cname, cvalue, exdays) {
  var d = new Date();
  d.setTime(d.getTime() + (exdays*24*60*60*1000));
  var expires = "expires="+d.toUTCString();
    
  // règle le pb des caractères interdits
  if ('btoa' in window) {
    cvalue = btoa(cvalue);
  }
    
  document.cookie = cname + "=" + cvalue + "; " + expires+';path=/';
}

La fonction prend trois paramètres : le nom du cookie, sa valeur et le nombre de jours avant que celui-ci n’expire. Petite explication pour la partie sur les caractères interdits. btoa permet d’encoder une chaine de caractère en base64, cela évite les problèmes de certains navigateurs avec les caractères non Unicode [en] (notamment Safari et les accents). Si la fonction est supportée dans le navigateur, on encode le contenu du cookie en base64.

On créé aussi une petite fonction saveCart pour sauvegarder d’un coup tout notre panier :

function saveCart(inCartItemsNum, cartArticles) {
  setCookie('inCartItemsNum', inCartItemsNum, 5);
  setCookie('cartArticles', JSON.stringify(cartArticles), 5);
}

On ne peut stocker que des chaines de caractères dans les cookies, on sérialise donc notre tableau cartArticles avec JSON.stringify avant de l’enregistrer.

getCookie

On créé ensuite la fonction pour récupérer les cookies :

function getCookie(cname) {
  var name = cname + "=";
  var ca = document.cookie.split(';');
  
  for(var i = 0; i < ca.length; i++) {
    var c = ca[i];
    while (c[0] == ' ') {
      c = c.substring(1);
    }
    
    if (c.indexOf(name) != -1) {
      if ('btoa' in window) {
        return atob(c.substring(name.length,c.length));
      }
      else {
        return c.substring(name.length,c.length);
      }
    }
  }
  return false;
}

Cette fonction prend en paramètre le nom du cookie et retourne sa valeur. Si btoa est supporté, cela veut dire qu’on a enregistré du base64 dans le cookie, on le retransforme alors en texte avec la fonction atob, sinon, on récupère simplement le texte non encodé.

Récupérer les articles et peupler le widget

Nous allons commencer par le plus simple : l’initialisation des variables, la récupération des informations dans les cookies et leur affichage. Nous verrons ensuite comment ajouter de nouveaux articles au panier.

Dès que nous changeons de page, nous devons récupérer tous les articles stockés dans les cookies et les placer dans le widget.

// variables pour stocker le nombre d'articles et leurs noms
var inCartItemsNum;
var cartArticles;

// affiche/cache les éléments du panier selon s'il contient des produits
function cartEmptyToggle() {
  if (inCartItemsNum > 0) {
    $('#cart-dropdown .hidden').removeClass('hidden');
    $('#empty-cart-msg').hide();
  }
  
  else {
    $('#cart-dropdown .go-to-cart').addClass('hidden');
    $('#empty-cart-msg').show();
  }
}

// récupère les informations stockées dans les cookies
inCartItemsNum = parseInt(getCookie('inCartItemsNum') ? getCookie('inCartItemsNum') : 0);
cartArticles = getCookie('cartArticles') ? JSON.parse(getCookie('cartArticles')) : [];

cartEmptyToggle();

// affiche le nombre d'article du panier dans le widget
$('#in-cart-items-num').html(inCartItemsNum);

// hydrate le panier
var items = '';
cartArticles.forEach(function(v) {
   items += '<li id="'+ v.id +'"><a href="'+ v.url +'">'+ v.name +'<br><small>Quantité : <span class="qt">'+ v.qt +'</span></small></a></li>';
});

$('#cart-dropdown').prepend(items);

Aux lignes 19 et 20 on utilise des conditions ternaires. Ligne 19, si le cookie ne retourne rien (il est vide ou inexistant), on attribue 0 par défaut à la variable. De plus, on s’assure à la ligne 19 que notre valeur est bien un entier et non une chaine de caractères. À la ligne 20, si le cookie ne retourne rien, on créé un objet vide. Vous remarquerez aussi à la ligne 20 qu’on parse la valeur récupérée du cookie avec JSON.parse, c’est parce que nous avons encodé le tableau d’articles en json dans la fonction saveCart.

À la ligne 30, vous notez que nous stockons d’abord nos articles dans une variable avant de les ajouter au DOM. On pourrait placer les éléments un par un dans le DOM en mettant prepend dans la boucle, mais c’est beaucoup moins performant que de le faire en une seule insertion.

Vous constaterez aussi à la ligne 30 que le nom des article est un lien qui mène directement à la fiche produit. Cela permet à l’internaute de rapidement retrouver toutes les informations à propos d’un produit qu’il a mis dans son panier.

Ajout d’un article au panier

Lorsque l’on clique sur le bouton d’ajout au panier #add-to-cart que nous avons mis en place plus haut, le JavaScript d’ajout s’exécute. Le code ci-dessous vient se placer à la suite du précédent :

[…]

// click bouton ajout panier
$('.add-to-cart').click(function() {
  
  // récupération des infos du produit
  var $this = $(this);
  var id = $this.attr('data-id');
  var name = $this.attr('data-name');
  var price = $this.attr('data-price');
  var weight = $this.attr('data-weight');
  var url = $this.attr('data-url');
  var qt = parseInt($('#qt').val());
  inCartItemsNum += qt;
  
  // mise à jour du nombre de produit dans le widget
  $('#in-cart-items-num').html(inCartItemsNum);
  
  var newArticle = true;
  
  // vérifie si l'article est pas déjà dans le panier
  cartArticles.forEach(function(v) {
    // si l'article est déjà présent, on incrémente la quantité
    if (v.id == id) {
      newArticle = false;
      v.qt += qt;
      $('#'+ id).html('<a href="'+ url +'">'+ name +'<br><small>Quantité : <span class="qt">'+ v.qt +'</span></small></a>');
    }
  });
  
  // s'il est nouveau, on l'ajoute
  if (newArticle) {
    $('#cart-dropdown').prepend('<li id="'+ id +'"><a href="'+ url +'">'+ name +'<br><small>Quantité : <span class="qt">'+ qt +'</span></small></a></li>');
    
    cartArticles.push({
      id: id,
      name: name,
      price: price,
      weight: weight,
      qt: qt,
      url: url
    });
  }

  // sauvegarde le panier
  saveCart(inCartItemsNum, cartArticles);

  // affiche le contenu du panier si c'est le premier article
  cartEmptyToggle();
});

Les commentaires sont assez explicites, rien de spécial jusqu’à la ligne 19 si ce n’est que l’on cache $(this) dans une variable pour les performances. À la ligne 19, on initialise notre variable permettant de savoir si l’article est nouveau où s’il est déjà dans le panier (c’est basé sur l’id, nous en avons parlé plus haut rappelez-vous).

Si l’id du produit est déjà présent dans l’array (ligne 24), c’est que l’article n’est pas nouveau, on passe donc la variable à false, on incrémente la quantité de cet article dans le tableau et on met à jour le widget. Sinon, la variable newArticle reste vraie, on ajoute alors l’article au widget et on fait un push pour ajouter le nouvel article à cartArticles.

Rendu de la page panier

Avant d’attaquer cette partie, il est important d’avoir à l’esprit que l’ordinateur exécute ses calculs en base 2. Ainsi, chaque nombre se voit attribuer un espace mémoire fini. Cette contrainte résulte en de nombreux arrondis lors des calculs sur les nombres, lesquels peuvent impacter le résultat final… Particulièrement en JavaScript. Pour plus de détails techniques, voici une explication assez synthétique sur StackOverflow [en].

// si on est sur la page ayant pour url monsite.fr/panier/
if (window.location.pathname == '/panier/') {
  var items = '';
  var subTotal = 0;
  var total;
  var weight = 0;
  
  /* on parcourt notre array et on créé les lignes du tableau pour nos articles :
  * - Le nom de l'article (lien cliquable qui mène à la fiche produit)
  * - son prix
  * - la dernière colonne permet de modifier la quantité et de supprimer l'article
  *
  * On met aussi à jour le sous total et le poids total de la commande
  */
  cartArticles.forEach(function(v) {
    // opération sur un entier pour éviter les problèmes d'arrondis
    var itemPrice = v.price.replace(',', '.') * 1000;
    items += '<tr data-id="'+ v.id +'">\
             <td><a href="'+ v.url +'">'+ v.name +'</a></td>\
             <td>'+ v.price +'€</td>\
             <td><span class="qt">'+ v.qt +'</span> <span class="qt-minus">–</span> <span class="qt-plus">+</span> \
             <a class="delete-item">Supprimer</a></td></tr>';
    subTotal += v.price.replace(',', '.') * v.qt;
    weight += v.weight * v.qt;
  });

  // on reconverti notre résultat en décimal
  subTotal = subTotal / 1000;
  
  // On insère le contenu du tableau et le sous total
  $('#cart-tablebody').empty().html(items);
  $('.subtotal').html(subTotal.toFixed(2).replace('.', ','));
  
  // lorsqu'on clique sur le "+" du panier
  $('.qt-plus').on('click', function() {
    var $this = $(this);
    
    // récupère la quantité actuelle et l'id de l'article
    var qt = parseInt($this.prevAll('.qt').html());
    var id = $this.parent().parent().attr('data-id');
    var artWeight = parseInt($this.parent().parent().attr('data-weight'));

    // met à jour la quantité et le poids
    inCartItemsNum += 1;
    weight += artWeight;
    $this.prevAll('.qt').html(qt + 1);
    $('#in-cart-items-num').html(inCartItemsNum);
    $('#'+ id + ' .qt').html(qt + 1);
    
    // met à jour cartArticles
    cartArticles.forEach(function(v) {
        // on incrémente la qt
        if (v.id == id){
            v.qt += 1;
            
            // récupération du prix
            // on effectue tous les calculs sur des entiers
            subTotal = ((subTotal * 1000) + (parseFloat(v.price.replace(',', '.')) * 1000)) / 1000;
        }
    });
    
    // met à jour la quantité du widget et sauvegarde le panier
    $('.subtotal').html(subTotal.toFixed(2).replace('.', ','));
    saveCart(inCartItemsNum, cartArticles);
  });
  
  // quantité -
  $('.qt-minus').click(function() {
    var $this = $(this);
    var qt = parseInt($this.prevAll('.qt').html());
    var id = $this.parent().parent().attr('data-id');
    var artWeight = parseInt($this.parent().parent().attr('data-weight'));

    if (qt > 1) {
      // maj qt
      inCartItemsNum -= 1;
      weight -= artWeight;
      $this.prevAll('.qt').html(qt - 1);
      $('#in-cart-items-num').html(inCartItemsNum);
      $('#'+ id + ' .qt').html(qt - 1);
      
      cartArticles.forEach(function(v) {
          // on décrémente la qt
          if (v.id == id) {
              v.qt -= 1;
              
              // récupération du prix
              // on effectue tous les calculs sur des entiers
              subTotal = ((subTotal * 1000) - (parseFloat(v.price.replace(',', '.')) * 1000)) / 1000;
          }
      });
      
      $('.subtotal').html(subTotal.toFixed(2).replace('.', ','));
      saveCart(inCartItemsNum, cartArticles);
    }
  });
  
  // suppression d'un article
  $('.delete-item').click(function() {
    var $this = $(this);
    var qt = parseInt($this.prevAll('.qt').html());
    var id = $this.parent().parent().attr('data-id');
    var artWeight = parseInt($this.parent().parent().attr('data-weight'));
    var arrayId = 0;
    var price;
    
    // maj qt
    inCartItemsNum -= qt;
    $('#in-cart-items-num').html(inCartItemsNum);
    
    // supprime l'item du DOM
    $this.parent().parent().hide(600);
    $('#'+ id).remove();
    
    cartArticles.forEach(function(v) {
        // on récupère l'id de l'article dans l'array
        if (v.id == id) {
            // on met à jour le sous total et retire l'article de l'array
            // as usual, calcul sur des entiers
            var itemPrice = v.price.replace(',', '.') * 1000;
            subTotal -= (itemPrice * qt) / 1000;
            weight -= artWeight * qt;
            cartArticles.splice(arrayId, 1);
            
            return false;
        }
        arrayId++;
    });
    
    $('.subtotal').html(subTotal.toFixed(2).replace('.', ','));
    saveCart(inCartItemsNum, cartArticles);
    cartEmptyToggle();
  });
}

Vous remarquerez à la ligne 22 qu’on formate notre prix au format anglais (on remplace la virgule décimale par un point). Ça nous permet de le transformer en nombre et de faire des opérations dessus (ici une multiplication et une addition). On le reformate ensuite à la ligne 28 pour le rendre plus agréable aux yeux de nos chers clients. Vous noterez par ailleurs que nous veillons toujours à ce que qt et artWeight soient des nombres en récupérant leurs valeurs depuis le HTML. Nous utilisons la fonction parseInt pour nous en assurer – cette fonction de transtypage force la conversion des chaines de caractères en nombre et nous permet ainsi d’effectuer des opérations sur les variables.

Vous vous demandez peut-être également pourquoi on maintient une variable weight à jour avec le poids total de la commande alors que nous n’en faisons rien. Nous allons par la suite passer cette variable à la fonction qui calcule les frais de port. Nous pourrions simplement recharger la page et récupérer de nouveau le poids total comme nous le faisons à la ligne 23.

Néanmoins, sur Alphapole, j’ai décidé de rendre l’expérience la plus fluide possible. Aussi, tout le nécessaire est déjà chargé sur la page /panier/. Au clic sur le bouton « Passer la commande », le JavaScript masque le tableau de commande et fait apparaître le formulaire de validation de commande (nom, prénom, adresse…), puis présente le total TTC (port inclus) avec les différentes options de paiement.

Calcul des frais de port

Vous trouverez ma fonction de calcul des frais de port sur GitHub. Il suffit de l’appeler en lui passant en paramètres le poids en grammes de la commande et la zone postale de destination. La fonction renvoie alors le prix des frais de port pour l’envoi en Colissimo.

Pour déterminer la zone géographique de l’adresse de l’utilisateur, j’utilise un select avec les différentes zones. Vous en trouverez un exemple sur le gitHub également.

Le formulaire de validation de commande ne fait pas partie du panier à proprement parler, je n’expliquerai donc pas sa mise en place. J’utilise de l’ajax pour éviter tout rechargement de page et je sauvegarde les informations de l’utilisateur dans le localStorage. De cette manière, s’il revient plus tard sur le site, il n’a pas à rentrer de nouveau toutes ses informations. Par ailleurs, je trouve cela beaucoup plus ergonomique que la création obligatoire d’un compte.

La création d’un compte n’est pas justifiée pour des sites sur lesquels on n’achète pas de manière régulière. D’accord pour Amazon, mais sur un e-commerce ou l’on ne se rend peut-être qu’une seule fois, voire une fois tous les six mois, l’internaute est découragé par la création préalable de celui-ci. Enfin, même dans la mesure où il en a déjà un, il y a de grande chance qu’il ait oublié son mot de passe comme il ne l’utilise que rarement.

Pour la mise en place des moyens de paiement, je vous invite à lire mes articles sur la mise en place et l’automatisation de Paypal avec Paypal IPN et l’API ATOS qu’utilisent les banques françaises (CIC, Société Générale…).

N’hésitez pas à me faire part de vos retours d’expérience en commentaires. Si certains passages ne sont pas assez développés ou manquent de clarté, n’hésitez pas également.

Déjà 25 réponses, rejoignez la discussion !

  • Mathieu

    dit :

    Excellent ce code, merci énormément!

    Je voulais partager un problème qui m’a tenu en haleine un bon moment, il faut bien donner au suivant :

    Ayant eu besoin de récupérer le contenu du cookie pour une utilisation en php, j’ai éprouvé des problèmes au niveau du décodage en base64. Selon les caractères présents, le décodage était parfois partiel et donc, inutilisable.

    Voici la solution que j’ai trouvée pour la partie js :

    Deux fonctions à ajouter :

    function b64EncodeUnicode(str) {
    	return btoa( unescape( encodeURIComponent( str ) ) );
    }
    
    function b64DecodeUnicode(str) {
    	return decodeURIComponent( escape( atob( str ) ) );
    }
    

    Ensuite, remplacer les parties où le cookie est encodé/décodé :

     if ('btoa' in window) {
        cvalue = b64EncodeUnicode(cvalue);
      }
    
    if ('btoa' in window) {
       return decodeURIComponent( escape( atob(c.substring(name.length,c.length))));
    }
    

    Pour la partie php :

    $cookie_comm = $_COOKIE["cartArticles"];
    $removebase64 = base64_decode($cookie_comm);
    $commande_arr = json_decode($removebase64, true);
    

    Avec ça on a un bel array php qui permet de s’amuser très fort avec ses produits.

    • Buzut

      dit :

      Merci pour ton retour Mathieu !

      Je crois que tu as fais un petit oublis. J’imagine que dans le deuxième if ('btoa' in window) { on remplace atob par b64DecodeUnicode.

      • Mathieu

        dit :

        Bien en fait, non, ça fonctionne comme ça. C’est un copier/coller provenant directement de mon code. Ça fonctionne, mais je suis vraiment mal placé pour juger de la validité du code, moi et le JS on ne s’entend pas bien!

        Le passage en php, c’était pour quitter au plus vite le JS pour que je trouve mes repères pour compléter la procédure de paiement et enregistrer ces belles informations en SQL. Mais vraiment, merci encore pour ce panier efficace, bien mieux que les autres que j’ai essayés avec des sessions.

        • Buzut

          dit :

          Le code n’est pas invalide, en revanche, tu n’utilises jamais la fonction b64DecodeUnicode !
          Je suis ravi que mon article ait pu t’aider. Et encore merci de ton retour :)

  • Ruben

    dit :

    Salut Buzut déja merci pour le code et surtout pour les explications tres claires et détaillés. J’ai juste une petite interrogation, quid de la sécurité ? Parce que toutes les valeurs sont stockés dans le html en fait avec les attributs data, donc par exemple qu’est ce qui empeche un internaute de modifer par exemple le data-price et de mettre ce qu’il veut a la place. Le script fonctionnera quand meme et il se retrouvera a payer le prix qu’il veut non ?

    • Buzut

      dit :

      Salut Ruben et merci de ton commentaire !

      C’est un point très interessant que tu soulevés la. Tu as tout à fait raison, le client peut en effet modifier le code et payer le montant qu’il veut. Néanmoins, cela ne veut pas dire pour autant qu’il y a un risque pour toi.

      Le code est côté client, néanmoins rien ne t’empêche d’effectuer une validation côté serveur. Tu as l’ensemble de tes prix en base de données, lorsque tu reçois un paiement, tu compares le montant attendu au montant effectivement paye. S’il ne correspond pas le paiement n’est pas valide et ça déclenche par exemple l’envoie d’un email au client.

      A partir du moment ou tu peux détecter que le montant pour x produit correspond ou non au produit qui devrait être payé, tout est envisageable.

      Pour moi il faut envoyer un mail au client, une alerte à la personne en charge de ça et on termine la transaction manuellement (paiement du reste du, annulation…)

      • Ruben

        dit :

        Oui j’ai pensé a ca et du coup j’en suis arrivé à me dire que le plus simple et le plus logique, quitte à faire une verif coté serveur alors autant utiliser directement les données stockés cotés serveurs pour remplir le panier. Ou alors les stocker dans un fichier Json ou xml ou text. Mais juste faire en sorte qu’ils ne soient pas modifiables en gros, qu’est ce que tu en penses ?

        • Buzut

          dit :

          Le problème ce n’est pas l’endroit ou elles sont stockées (mes données sont stockees coté serveur et celui-ci génère le html).

          Même si tu as un fichier xml non modifiable, si le navigateur renvoie des données, il est possible pour l’internaute d’intercepter les données et de les renvoyer modifiées. Regarde du côté de la faille « tamper data ».

          Pour moi le plus sur cest vraiment la vérification côté serveur. Cela dit tu peux aussi la faire avant le paiement. Le navigateur ne renvoie que les références des produit et cest côté serveur que tu calcules le montant du panier… mais ce ne permet pas de gérer tous le panier en JS.

          • Ruben

            dit :

            Ah oui je suis con lol javais pas vu le truc comme ca. Donc au final c’est pas vraiment possible de faire un panier 100% javascript qui soit vraiment sur en fait ?
            Sinon merci pour tes conseils, tu as l’air vraiment passionné par le dev et ca m’interesserait bcp de pouvoir échanger un peu plus avec toi de temps en temps sur skype par exemple, parce que je débute dans la programmation, j’ai appris JS ya pas lgt, (je compte rejoindre l’ecole 42 l’année prochaine) et j’aimerais bien pouvoir échanger et partager avec quelqu’un les problemes que je rencontre en élaborant certains codes, ou en essayant de developper certaines idées etc..
            En tout cas continue comme ca c’est génial

  • d

    dit :

    Bonjour,

    Merci pour ce tuto mais est-il possible d’avoir les fichiers sources s’il vous plait ? j’avoue que je suis un peu perdue sur le point : quels fichiers créé-t-on et où est-ce qu’on met ces bouts codes s’il vous plait ?

    • Buzut

      dit :

      Bonjour,

      Malheureusement, je n’ai pas créé de dépôt ou zip avec les sources. J’ai noté de le faire quand j’aurais le temps. on a d’un côté le JS, il serait à placer dans main.js (ou autre nom) et à inclure aux pages. De l’autre, trois éléments distincts:

      – le code html qui définit chaque article,
      – le « widget » panier qui se place généralement sur tout les pages (en haut de page),
      – le panier à proprement parler qui est une page dédiée.

      J’espère que c’est un peu plus clair. Je m’efforcerai de faire un petit dépôt sur git, dès que j’ai le temps, pour pouvoir mieux visualiser comment tout cela s’articule !

      • Abdul Soro

        dit :

        bonsoir et bien moi c’est Abdul Soro suis dans la programmation web depuis peut et pour des besoins de mettre sur place un panier suis tombé sur cet article (superbe article au passage) mais malheureusement ne fonctionne pas chez moi j’ai sans doute mal suivi un étape donc ça sera vraiment génial si vous pouvez finaliser votre projet de zip

  • Nathalie

    dit :

    Bonjour,
    Tuto très intéressant mais j’ai reporté les codes un peu comme je pouvais et rien ne fonctionne.
    Ne connaissant pas le javascript, les fichiers source m’auraient bien aidée.
    Est-ce que l’on peut les trouver quelque part ?

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *