Ajouter des frais selon la méthode de paiement Prestashop

Bon aujourd’hui on va s’attaquer à un problème qu’on m’a déjà souvent demandé…. et que finalement j’avais pas vraiment pu apporter une réponse claire, car par défaut on ne peut pas le faire. Oui, il s’agit d’ajouter des frais en fonction de la méthode de paiement.



Le problème des frais de paiement
Bon imaginons… par exemple on prend le cas du client qui décide de payer avec Paypal, on va dire pour l’exemple qu’il aura 3.5% de frais sur la commande. Du coup, sur 100 CHF / EUR vous perdez 3.5 CHF / EUR mais sur une année imaginez que vous avez eu 1000 commandes… ça fait 3.500 CHF / EUR perdu dans le vide…. et ça fait mal… Où encore par exemple pour le traitement par virement… ça vous demande du temps de travail, vous pourriez aussi appliquer une taxe supplémentaire en conséquence.

Bon allez ! On va tenter de le faire ensemble  !
Pour cet exemple, je vais prendre la méthode virement, l’idée est donc d’ajouter des frais supplémentaires à la commande si l’utilisateur choisi le virement bancaire. Pour commencer il faut créer une catégorie (qui s’appelle par exemple Administration) dans votre back-office et la désactiver. Ensuite il faut créer un produit qui s’appelle par exemple « Frais virement » avec un montant de votre choix… (n’oubliez pas de désactiver la gestion des quantités pour ce produit et mémorisez son ID).

La catégorie administration est désactivée



Le produit de taxe avec son ID qui est 14

!!! Attention : dans tous les fichiers ci-dessous je vais utiliser l’id 14, il faudra absolument remplacer l’id par le vôtre !!!

A présent on va créer un fichier sous /override/controllers/OrderController.php pour modifier le comportement de la boutique, lorsqu’on arrive sur la sélection des méthodes de paiement. Ici on va supprimer la taxe dans le panier si elle existe.

Code de OrderController.php

<?php
ControllerFactory::includeController('ParentOrderController');

class OrderController extends OrderControllerCore
{

/* Payment step */
protected function _assignPayment()
{
global $orderTotal;

// -----------------------------------------------
// Webbax - 04.03.13 - retrait des frais du panier
global $cookie;
$Cart = new Cart($cookie->id_cart);
$products = $Cart->getProducts();
foreach($products as $p){
if($p['id_product']==14){
$Cart->deleteProduct(14);
Tools::redirect($_SERVER['REQUEST_URI']);
}
}
// -----------------------------------------------

// Redirect instead of displaying payment modules if any module are grefted on
Hook::backBeforePayment('order.php?step=3');

/* We may need to display an order summary */
self::$smarty->assign(self::$cart->getSummaryDetails());
self::$smarty->assign(array(
'total_price' => (float)($orderTotal),
'taxes_enabled' => (int)(Configuration::get('PS_TAX'))
));
self::$cookie->checkedTOS = '1';

parent::_assignPayment();
}

}

Ensuite on va modifier le traitement dans le fichier modules/bankwire/payment.php pour lui dire qu’il doit ajouter la taxe, si on a sélectionné la méthode virement bancaire.

<?php

/* SSL Management */
$useSSL = true;

include(dirname(__FILE__).'/../../config/config.inc.php');
include(dirname(__FILE__).'/../../header.php');
include(dirname(__FILE__).'/bankwire.php');

// -----------------------------------------------
// Webbax - 04.03.13 - retrait des frais du panier
global $cookie;
$Cart = new Cart($cookie->id_cart);
$products = $Cart->getProducts();
$find_cost = false;
foreach($products as $p){
if($p['id_product']==14){$find_cost=true;}
}
if(!$find_cost){
$Product = new Product(14,false,$cookie->id_lang);
$Cart->updateQty(1,$Product->id);
Tools::redirect($_SERVER['REQUEST_URI']);
}
// -----------------------------------------------

if (!$cookie->isLogged(true))
Tools::redirect('authentication.php?back=order.php');
elseif (!Customer::getAddressesTotalById((int)($cookie->id_customer)))
Tools::redirect('address.php?back=order.php?step=1');

$bankwire = new BankWire();
echo $bankwire->execPayment($cart);

include_once(dirname(__FILE__).'/../../footer.php');

Maintenant il reste à affiner le niveau visuel, déjà il faut ajouter dans le fichier modules/bankwire/payment.tpl un petit texte qui prévient que la méthode de paiement virement va ajouter 2.5 CHF de frais (c’est juste du cosmétique visuel).

<p class="payment_module">
<a href="{$this_path_ssl}payment.php" title="{l s='Pay by bank wire' mod='bankwire'}">
<img src="{$this_path}bankwire.jpg" alt="{l s='Pay by bank wire' mod='bankwire'}" width="86" height="49"/>
{l s='Pay by bank wire (order process will be longer)' mod='bankwire'} {l s='+ 2.50 CHF de frais' mod='bankwire'}
</a>
</p>

Ensuite dans le fichier modules/blockcart/blockcart.tpl on va retirer le bouton suppression dans le panier, sur l’article de la taxe.

Il suffit de trouver cette ligne (vers la ligne 68) :

<span class="remove_link">{if !isset($customizedDatas.$productId.$productAttributeId)}<a rel="nofollow" class="ajax_cart_block_remove_link" href="{$link->getPageLink('cart.php')}?delete&id_product={$product.id_product}&ipa={$product.id_product_attribute}&token={$static_token}" title="{l s='remove this product from my cart' mod='blockcart'}"> </a>{/if}</span>

Et de la remplacer par :

{if $product.id_product!=14}
<span class="remove_link">{if !isset($customizedDatas.$productId.$productAttributeId)}<a rel="nofollow" class="ajax_cart_block_remove_link" href="{$link->getPageLink('cart.php')}?delete&id_product={$product.id_product}&ipa={$product.id_product_attribute}&token={$static_token}" title="{l s='remove this product from my cart' mod='blockcart'}"> </a>{/if}</span>
{/if}

Puis enfin pour finaliser, il faut enlever aussi la suppression de la taxe dans la page panier, pour cela on va modifier le fichier /themes/prestashop/shopping-cart-product-line.tpl et on va encapsuler dans un div le contenu suivant (vers la ligne 51) :

<div>
<a rel="nofollow" class="cart_quantity_delete" id="{$product.id_product}_{$product.id_product_attribute}" href="{$link->getPageLink('cart.php', true)}?delete&id_product={$product.id_product|intval}&ipa={$product.id_product_attribute|intval}&token={$token_cart}" title="{l s='Delete'}"><img src="{$img_dir}icon/delete.gif" alt="{l s='Delete'}" class="icon" width="11" height="13" /></a>
</div>
<div id="cart_quantity_button" style="float:left;">
<a rel="nofollow" class="cart_quantity_up" id="cart_quantity_up_{$product.id_product}_{$product.id_product_attribute}" href="{$link->getPageLink('cart.php', true)}?add&id_product={$product.id_product|intval}&ipa={$product.id_product_attribute|intval}&token={$token_cart}" title="{l s='Add'}"><img src="{$img_dir}icon/quantity_up.gif" alt="{l s='Add'}" width="14" height="9" /></a><br />
{if $product.minimal_quantity < ($product.cart_quantity-$quantityDisplayed) OR $product.minimal_quantity <= 1}
<a rel="nofollow" class="cart_quantity_down" id="cart_quantity_down_{$product.id_product}_{$product.id_product_attribute}" href="{$link->getPageLink('cart.php', true)}?add&id_product={$product.id_product|intval}&ipa={$product.id_product_attribute|intval}&op=down&token={$token_cart}" title="{l s='Subtract'}">
<img src="{$img_dir}icon/quantity_down.gif" alt="{l s='Subtract'}" width="14" height="9" />
</a>
{else}
<a class="cart_quantity_down" style="opacity: 0.3;" href="#" id="cart_quantity_down_{$product.id_product}_{$product.id_product_attribute}" title="{l s='You must purchase a minimum quantity of '}{$product.minimal_quantity}{l s=' of this product.'}">
<img src="{$img_dir}icon/quantity_down.gif" width="14" height="9" alt="{l s='Subtract'}" />
</a>
{/if}
</div>
<input type="hidden" value="{if $quantityDisplayed == 0 AND isset($customizedDatas.$productId.$productAttributeId)}{$customizedDatas.$productId.$productAttributeId|@count}{else}{$product.cart_quantity-$quantityDisplayed}{/if}" name="quantity_{$product.id_product}_{$product.id_product_attribute}_hidden" />
<input size="2" type="text" class="cart_quantity_input" value="{if $quantityDisplayed == 0 AND isset($customizedDatas.$productId.$productAttributeId)}{$customizedDatas.$productId.$productAttributeId|@count}{else}{$product.cart_quantity-$quantityDisplayed}{/if}"  name="quantity_{$product.id_product}_{$product.id_product_attribute}" />

Ce qui donnera :

{* div special *}
<div {if $product.id_product==14}style="display:none;"{/if}>
<div>
<a rel="nofollow" class="cart_quantity_delete" id="{$product.id_product}_{$product.id_product_attribute}" href="{$link->getPageLink('cart.php', true)}?delete&id_product={$product.id_product|intval}&ipa={$product.id_product_attribute|intval}&token={$token_cart}" title="{l s='Delete'}"><img src="{$img_dir}icon/delete.gif" alt="{l s='Delete'}" class="icon" width="11" height="13" /></a>
</div>
<div id="cart_quantity_button" style="float:left;">
<a rel="nofollow" class="cart_quantity_up" id="cart_quantity_up_{$product.id_product}_{$product.id_product_attribute}" href="{$link->getPageLink('cart.php', true)}?add&id_product={$product.id_product|intval}&ipa={$product.id_product_attribute|intval}&token={$token_cart}" title="{l s='Add'}"><img src="{$img_dir}icon/quantity_up.gif" alt="{l s='Add'}" width="14" height="9" /></a><br />
{if $product.minimal_quantity < ($product.cart_quantity-$quantityDisplayed) OR $product.minimal_quantity <= 1}
<a rel="nofollow" class="cart_quantity_down" id="cart_quantity_down_{$product.id_product}_{$product.id_product_attribute}" href="{$link->getPageLink('cart.php', true)}?add&id_product={$product.id_product|intval}&ipa={$product.id_product_attribute|intval}&op=down&token={$token_cart}" title="{l s='Subtract'}">
<img src="{$img_dir}icon/quantity_down.gif" alt="{l s='Subtract'}" width="14" height="9" />
</a>
{else}
<a class="cart_quantity_down" style="opacity: 0.3;" href="#" id="cart_quantity_down_{$product.id_product}_{$product.id_product_attribute}" title="{l s='You must purchase a minimum quantity of '}{$product.minimal_quantity}{l s=' of this product.'}">
<img src="{$img_dir}icon/quantity_down.gif" width="14" height="9" alt="{l s='Subtract'}" />
</a>
{/if}
</div>
<input type="hidden" value="{if $quantityDisplayed == 0 AND isset($customizedDatas.$productId.$productAttributeId)}{$customizedDatas.$productId.$productAttributeId|@count}{else}{$product.cart_quantity-$quantityDisplayed}{/if}" name="quantity_{$product.id_product}_{$product.id_product_attribute}_hidden" />
<input size="2" type="text" class="cart_quantity_input" value="{if $quantityDisplayed == 0 AND isset($customizedDatas.$productId.$productAttributeId)}{$customizedDatas.$productId.$productAttributeId|@count}{else}{$product.cart_quantity-$quantityDisplayed}{/if}"  name="quantity_{$product.id_product}_{$product.id_product_attribute}" />
{* div special *}
</div>

Bon au cas où vous en avez besoin, je vous donnes les fichiers que j’ai modifié, vous pouvez les télécharger ici.

Regardons ensemble le résultat

N’oubliez pas de désactiver le cache et de forcer la compilation, sinon vous ne verrez pas les changements.
Ici on voit qu’il n’y a pas encore la taxe, mais on prévient l’utilisateur que celle-ci implique 2.50 CHF de frais.

Les frais sont ajoutés et on ne peut pas les supprimer manuellement, par contre si l’utilisateur revient à la sélection des paiements, la taxe est retirée.

Dans le récapitulatif du panier on ne peut pas supprimer la taxe.

Une fois la commande validée, on retrouvera dans le détail, une ligne supplémentaire dédiée à la taxe.

Et si je veux faire ça pour une autre méthode de paiement ?
Le raisonnement est identique, il faudra « juste »… (enfin façon de parler)… adapter les conditions déjà en place et faire le même type de changement dans le module concerné. Ici j’ai pris le module virement, parce que c’est un bon exemple et qu’il fonctionne assez simplement.

Bilan
Voilà la méthode que je pourrai suggérer pour la gestion des frais supplémentaires liés à la plateforme de paiement. Ce n’est pas parfait, parce que je pense qu’après il faudra pratiquer quelques exclusions, du style la taxe risque de se retrouver dans les meilleures ventes… vous voyez le genre ? Mais sur le principe je pense que c’est la bonne piste à explorer et je me suis dit que peut-être ça aiderait certains à se dépatouiller et alléger un peu leurs charges 😉 !

27 commentaires sur “Ajouter des frais selon la méthode de paiement Prestashop”

  1. Attention cette pratique est strictement interdite pour les boutiques de droit français.

    Interdiction formelle d’appliquer des frais supplémentaires en fonction du moyen de paiement choisi.

      1. Oui, prenons le cas par exemple du paiement par facture, il y’a des frais supplémentaires qui peuvent être générés (c’est une méthode de paiement de luxe pour le client).
        Payer par carte de crédit ou par facture c’est pas les mêmes risques et pas les mêmes institutions derrière… je vois mal le marchand supporter tout à sa charge, dans le cas du paiement par facture il vendrait à perte.

    1. Et pourtant, j’en ai vu des clients français me demander comment faire 😉
      Bon en même temps… et si on gonflait artificiellement les frais de ports… (ou même avec la taxe de manutention) là ça serait permis et ça reviendrait au même.
      Certaines règlementations me font quand même bien marrer… car elles sont d’un non-sens….

  2. Excellent tuto!
    Fonctionne a merveille pour les virements, cependant je n’arrive pas à l’appliquer pour paypal. Pour la version 2.4 du module il suffisait de remplacer

    $paymentAmount = (float)($cart->getOrderTotal());

    Par :

    $amount = round((($cart->getOrderTotal() * 3.4) / 100) + 0.25, 2);
    $amount = $amount + $cart->getOrderTotal();
    $paymentAmount = (float)($amount);

    Mais la 3.4 fonctionne bien différemment.
    Avez-vous trouvez une solution?

      1. Bonjour,
        en quoi la logique est elle différente sur la 1.5 ? J’ai repris le code en remplaçant « $Cart->deleteProduct(93); » par « $this->context->cart->deleteProduct(93); », et ça semble fonctionner correctement (ou alors j’ai pas vu le bug), sauf qu’après avoir choisi mon moyen de paiement (dans mon cas le chèque), je suis redirigé vers la page « http://www.monsite.ch/index.php?controller=module/cheque/payment » au lieu de « http://www.monsite.ch/module/cheque/payment », et je ne comprends d’ailleurs pas d’où vient le pb.

        Au fait, à quoi sert le « Tools::redirect($_SERVER[‘REQUEST_URI’]); » dans OrderController.php et payment.php ? Sont ils nécessaires dans la version 1.5 ?

        Merci,
        Julien

        Quand ça fonctionnera je mettrai mes modifications pour la 1.5 sur ce site si ça vous intéresse.

          1. Bonjour,

            C’est exactement pour ce genre de chose, il suffit d’une légère variante pour que la modification ne fonctionne pas.

            Entre la version 1.4 et 1.5 il y’a trop de changements, pour assurer un résultat fonctionnel il faudrait que je révise l’entier des processus sous 1.5 et que je les teste.

            Dans votre cas rien n’empêche de vous inspirer de la logique… mais il faudra certainement prévoir des adaptations.

        1. Bon, à priori après quelques adaptations ça fonctionne. Ceux qui vont faire les modifs pour la 1.5, si vous constatez un bug, n’hésitez pas à faire remonter l’info qu’on cherche une correction.
          Pour ma part j’ai modifié le module de paiement par chèque (contrairement au tuto destiné au module virement bancaire, mais c’est le même principe.)

          J’ai commenté mes modifs pour la compréhension, donc n’hésitez pas à les enlever pour que ce soit plus propre.
          * Fichier override/controllers/front/OrderController.php :

          // -----------------------------------------------
          // Webbax - 04.03.13 - retrait des frais du panier
          //global $cookie; Commenté car dans la 1.5 on se sert de $this->context (à confirmer).
          //$Cart = new Cart($cookie->id_cart); Cette ligne et celle d'en dessous sont obsolètes en 1.5
          //$products = $Cart->getProducts();
          $products = $this->context->cart->getProducts(); //remplace les deux lignes au dessus
          foreach($products as $p){
              if($p['id_product']==93){//93 = id du produit créé pour simuler les frais
                  //$Cart->deleteProduct(93); Cette ligne est obsolète en 1.5
                  $this->context->cart->deleteProduct(93); //remplace la ligne du dessus
                  //Tools::redirect($_SERVER['REQUEST_URI']); Cette ligne est commentée car elle me faisait un bug
              }
          }
          // -----------------------------------------------
          

          * Fichier modules/cheque/controllers/front/payment.php : (n’oubliez pas que dans mon cas c’est le module de chèque qui est modifié, donc modifiez le module adapté à votre situation)

          // -----------------------------------------------
          // Webbax - 04.03.13 - retrait des frais du panier
          //global $cookie; Commenté car dans la 1.5 on se sert de $this->context (à confirmer).
          //$Cart = new Cart($cookie->id_cart); j'ai commenté cette ligne car dans le module de paiement par chèque il y a un peu plus haut "$cart = $this->context->cart;" avec $cart écrit avec un "c" minuscule.
          $products = $cart->getProducts(); //$cart avec un "c" minuscule car je me sert de "$cart = $this->context->cart;" écrit plus haut dans le code du module chèque.
          $find_cost = false;
          foreach($products as $p){
          	if($p['id_product']==93){$find_cost=true;} //93 = id du produit créé pour simuler les frais
          }
          if(!$find_cost){
          	$Product = new Product(93,false,(int)$this->context->language->id);// dans la 1.5 "(int)$this->context->language->id" remplace "$cookie->id_lang"
          	$cart->updateQty(1,$Product->id); //$cart avec un "c" minuscule car je me sert de "$cart = $this->context->cart;" écrit plus haut dans le code du module chèque.
          	//Tools::redirect($_SERVER['REQUEST_URI']); Remplacé par les 2 lignes suivantes car pour moi ça ne fonctionnait pas
          	$lien = Context::getContext()->link->getModuleLink('cheque', 'payment'); //je définis le nouveau lien, soit le controller "payment" du module "cheque". A adapter selon votre cas
          	Tools::redirect($lien); //remplace donc la ligne "Tools::redirect($_SERVER['REQUEST_URI']);"
          }
          // -----------------------------------------------
          

          * Modification des templates :
          Dans la 1.5 on utilise toujours « {if $product.id_product==14}{/if} » pour choisir ce qui est affiché ou non si le produit a l’id 14 (id du produit pour simuler les frais liés au paiement dans cet exemple, à remplacer donc par l’id de votre produit).

          Il y a les fichiers tpl de la 1.5 ne sont pas exactement les mêmes que ceux de l’exemple, et je ne vous donne pas mes modifications car mon thème est entièrement refait, donc ça ne vous servirait pas, mais c’est facile à comprendre.

          Ceci dit, dans le panier (shopping-cart-product-line.tpl), en plus de supprimer la croix qui sert à enlever le produit simulant les frais liés au paiement, pensez à supprimer le lien vers la fiche produit (uniquement pour le produit simulant les frais liés au paiement hein) qui se trouve sur le titre et la description je crois, ainsi que la photo car personnellement je me vois mal mettre une photo sur ce produit 😉

          Pareil dans le fichier blockcart.tpl, on enlève la croix et le lien vers la fiche produit. Et attention, dans la 1.5, il se peut que blockcart.tpl se trouve dans themes/votretheme/modules/blockcart/ qui surcharge donc modules/blockcart

          Voilà, donc si il y a des bugs faites le remonter.

          Julien

          1. Bonjour,

            Merci pour votre précieuse contribution, qui pourra peut-être aider d’autres marchands.

            A tout bientôt !

          2. Merci beaucoup pour cette mise à jour de la version 1.5
            J’ai juste un souci avec le panier qui me dit qu’il y 2 produits au lieu d’un. ( un produit + les frais qu’il considère comme un produit)
            Est ce le OrderController qui ne fait pas son rôle ?

          3. Bonjour,

            Désolé, pour cela je ne peux apporter de précisions, car je n’ai pas codé le processus pour la version 1.5, il faudrait que je le re-code complètement sous cette version pour garantir un fonctionnement.

            Si jamais vous pouvez ouvrir un topic sur le forum Prestashop, tout en faisant un lien vers ce billet.

    1. Il faut utiliser la même logique, mais clairement que le code est différent…

      A noter aussi que ce code était compatible pour Prestashop 1.4, pour la version 1.5 il faut procéder encore autrement.

    1. Hello,

      Respect à toi si tu as réussi à reprendre ma logique pour Prestashop 1.5

      Actuellement sous les versions courantes je conseille de passer par un module qui permet d’ajouter des frais en fonction de la méthode, il en existe quelques-uns hors Prestashop Addons.

      A bientôt !

Laisser un commentaire

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