Le point sur les incompatibilités entre la dernière version de Prestashop 1.7.6 et certains modules utilisant des scripts PHP sans faire appel à un contrôleur Prestashop dûment déclaré. Si vous écoutez le support Prestashop, il parait que le module Sips 1.0 – Atos Worldline v5.0.1 (05/07/2019) est compatible avec Prestashop v1.7.6.0 (10/07/2019). De mon coté je ne fais pas ce constat : la validation des paiements ne fonctionne pas et voici comment la corriger.
Comportement anormal
Suite à un paiement d’un panier via le module Atos, la commande est créée mais le paiement n’est jamais validé en automatique. Les logs du serveur laissent apparaitre le message suivant.
1 2 3 4 5 6 7 |
2019/08/19 15:43:08 [error] 20774#20774: *454 FastCGI sent in stderr: "PHP message: PHP Fatal error: Uncaught Error: Call to a member function get() on null in /var/www/site/classes/Tools.php:801 Stack trace: #0 /var/www/site/classes/Tools.php(773): ToolsCore::getContextLocale(Object(Context)) #1 /var/www/site/classes/PaymentModule.php(421): ToolsCore::displayPrice(1, Object(Currency), false) #2 /var/www/site/modules/atos/validation.php(138): PaymentModuleCore->validateOrder(123, '2', 1, 'Atos', 'Transaction ID:...', Array, NULL, false, 'c19fdedf2570553...') #3 {main} thrown in /var/www/site/classes/Tools.php on line 801" while reading response header from upstream, client: 160.92.133.135, server: www.site.com, request: "POST /modules/atos/validation.php HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "www.site.com" |
Cela ressemble énormément au bug rencontré par des utilisateurs Prestashop en Juillet dernier sur cette page.
La page https://www.site.com/modules/atos/validation.php est en cause, et elle est utilisée par le serveur bancaire pour indiquer à la boutique Prestashop si le paiement est validé ou pas.
La mauvaise pratique fautive
Dans certaines versions du module Atos plus anciennes (la 3.1.3 par exemple) on déclenchait cette validation par l’une des URL suivantes :
- Avec URL rewriting : https://www.site.com/module/atos/validation (notez l’absence de s à module)
- Sans URL rewriting : https://www.site.com/index.php?fc=module&module=atos&controller=validation
Avec ce type d’URL on appelle le contrôleur validation dans le module atos. C’est cette méthode d’appel qui est conseillée et qui devra être adoptée à nouveau par le module Atos. Elle n’aurait jamais du être abandonnée en premier lieu. Visiblement Prestashop a resserré la vis avec sa dernière mise à jour, et la mauvaise pratique de programmation qui passait jusque là n’est plus possible.
Pour déclarer ce contrôleur de validation par nous-mêmes nous alons créer le fichier /modules/atos/controllers/front/validation.php (comme sur l’ancienne version Atos 3.1.3). Ce fichier contiendra le contenu de /modules/atos/validation.php avec quelques modifications.
J’ai essayé de tourner avec Atos 3.1.3 sur Prestashop 1.7.6.0 sans succès car cette version du modune n’est pas compatible PS1.7 et si la configuration est possible, le paiement CB n’apparait pas au client. Pas plus de chances avec Atos 3.2.0 qui reprend déjà cette mauvaise pratique fautive.
La solution : créer un contrôleur de module
Il semblerait que le problème soit assez récent, et commun avec un grand nombre de plateformes de paiement. D’ici quelques temps, les éditeurs devraient mettre à jour leurs modules pour permettre le fonctionnement avec les dernières versions de Prestashop.
Visiblement le forumeur c00lsp0t sur prestashop.com a réussi à faire fonctionner son module de paiement clicandpay avec sa banque Crédit du Nord (source). Ses explications sont, dans une certaine mesure, transposables à Atos.
Voici comment j’ai procédé pour faire fonctionner Atos Wordline 1.0 v5.0.1 avec Prestashop 1.7.6.0.
L’idée est de créer un contrôleur dans le module Prestashop. Il y a déjà un contrôleur /modules/atos/controllers/front/orderconfirmation.php et on va lui rajouter /modules/atos/controllers/front/validation.php .
/var/www/site/modules/atos/controllers/front/validation.php :
Toutes les fonctions dirname(__FILE__) devront être surveillées pour rajouter ../.. juste après car le contrôleur est 2 dossiers plus profond que le script validation.php original. Sans cela il est bien possible que vous receviez un email du type Error reading pathfile (/var/www:site/modules/atos/controllers/front/pathfile) .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
<?php class AtosValidationModuleFrontController extends ModuleFrontController { public function initContent() { parent::initContent(); $this->template = _PS_MODULE_DIR_.'atos/views/templates/front/validation.tpl'; } public function postProcess() { /* Includes désactivés include(dirname(__FILE__).'/../../config/config.inc.php'); if (version_compare(_PS_VERSION_, '1.5', '<')) include(dirname(__FILE__).'/../../init.php'); include(dirname(__FILE__).'/atos.php'); */ if (!Tools::getValue('DATA')) { // Désactivation de l'exception code 500 (tester de réactiver) //throw new Exception('error in atos module: data is required'); } else { $datas = Tools::getValue('DATA'); $error_behavior = (int)Configuration::get('ATOS_ERROR_BEHAVIOR'); $atos = new Atos(); $is_win = (Tools::strtoupper(Tools::substr(PHP_OS, 0, 3)) === 'WIN'); $exec = $atos->bin_dir.'response'.(((int)$is_win === 1) ? '.exe' : ''); $result = exec($exec.' pathfile='.dirname(__FILE__).'/../../pathfile message='.preg_replace('#[^a-z0-9]#Ui', '', $datas)); $result_array = explode('!', $result); if (!count($result_array) || !isset($result_array[3]) || !isset($result_array[6])) { Mail::Send(Configuration::get('PS_LANG_DEFAULT'), 'notification', $atos->l('Atos notification'), array('message' => $atos->l('error in atos payment module: can\'t execute request')), Configuration::get('ATOS_NOTIFICATION_EMAIL'), null, null, null, null, null, dirname(__FILE__).'/../../mails/'); } elseif ($result_array[1] == -1) { Mail::Send(Configuration::get('PS_LANG_DEFAULT'), 'notification', $atos->l('Atos notification'), array('message' => $atos->l('error in atos payment module:').' '.$result_array[2]), Configuration::get('ATOS_NOTIFICATION_EMAIL'), null, null, null, null, null, dirname(__FILE__).'/../../mails/'); } else { $total_paid = ($result_array[5] / 100); $message = $atos->l('Transaction ID:').' '.$result_array[6].'<br />'."\n". $atos->l('Payment mean:').' '.$result_array[7].'<br />'."\n". $atos->l('Payment has began at:').' '.$result_array[8].'<br />'."\n". $atos->l('Payment received at:').' '.$result_array[10].' '.$result_array[9].'<br />'."\n". $atos->l('Authorization ID:').' '.$result_array[13].'<br />'."\n". $atos->l('Currency:').' '.$result_array[14].'<br />'."\n". $atos->l('Customer IP address:').' '.$result_array[29].'<br />'."\n". $atos->l('Cart ID:').' '.$result_array[22].'<br />'."\n\n". $atos->l('Atos Real Paid:').' '.$total_paid.'<br />'."\n\n". $atos->l('Atos Version:').' '.$atos->version.'<br />'."\n"; $order_state = _PS_OS_PAYMENT_; /* Checking whether merchant ID is OK */ $merchant_id = Configuration::get('ATOS_MERCHANT_ID'); if ($result_array[3] != $merchant_id) { $order_state = _PS_OS_ERROR_; $msg = ' ('.$result_array[3].' '.$atos->l('should be').' '.$merchant_id.')'; $message .= '<span style="color: red;">'.$atos->l('Merchant ID is not valid').$msg.'</span>'."\n"; } /* Checking for currency */ if ($order_state == _PS_OS_PAYMENT_) { $cart = new Cart((int)$result_array[22]); $currencies = array(1 => '978'); if (isset($currencies[$cart->id_currency])) { if ($currencies[$cart->id_currency] != Tools::strtoupper($result_array[14])) { $order_state = _PS_OS_ERROR_; $message .= '<span style="color: red;">'.$atos->l('Currency is not the right one (should be ').$currencies[$cart->id_currency].')</span>'."\n"; } } } /* Checking for bank code response */ if ($order_state == _PS_OS_PAYMENT_) { $response_code = (int)$result_array[11]; switch ($response_code) { case 3: $message .= '<span style="color: red;">'.$atos->l('Merchand ID is not valid').'</span>'."\n"; $order_state = _PS_OS_ERROR_; break; case 5: $message .= '<span style="color: red;">'.$atos->l('Bank has rejected payment').'</span>'."\n"; $order_state = _PS_OS_ERROR_; break; case 12: case 17: die; case 30: $message .= '<span style="color: red;">'.$atos->l('Format error').'</span>'."\n"; $order_state = _PS_OS_ERROR_; break; case 34: $message .= '<span style="color: red;">'.$atos->l('Bank said that transaction might be fraudulous').'</span>'."\n"; $order_state = _PS_OS_ERROR_; break; case 75: $message .= '<span style="color: red;">'.$atos->l('Customer has exceeded max tries for its card number').'</span>'."\n"; $order_state = _PS_OS_ERROR_; break; case 90: $message .= '<span style="color: red;">'.$atos->l('Bank server was unavailable').'</span>'."\n"; $order_state = _PS_OS_ERROR_; break; case 94: $message .= '<span style="color: red;">'.$atos->l('Duplicate Order, Order not registered').'</span>'."\n"; $order_state = _PS_OS_ERROR_; break; } } $customer = new Customer((int)$cart->id_customer); if ($order_state == _PS_OS_PAYMENT_ || $error_behavior == '0') { $total_paid = ($result_array[5] / 100); $atos->validateOrder((int)$result_array[22], $order_state, $total_paid, $atos->displayName, $message, array(), null, false, $customer->secure_key); if (version_compare(_PS_VERSION_, '1.5.0.0') >= '0') { $amount = ($result_array[5] / 100); $order_id = Order::getOrderByCartId((int)$result_array[22]); $order = new Order((int)$order_id); if ($order_state == _PS_OS_PAYMENT_) { $order->valid = 1; $order->save(); } $id_order_payment = Db::getInstance()->getValue('SELECT id_order_payment FROM `'._DB_PREFIX_.'order_payment` WHERE `order_reference` LIKE \'%'.pSQL($order->reference).'%\''); if ($id_order_payment == false) $order->addOrderPayment($amount, null, $result_array[6]); else { $order_payment = new OrderPayment((int)$id_order_payment); $order_payment->transaction_id = $result_array[6]; $order_payment->save(); } } } elseif ($error_behavior == 1) { Mail::Send(Configuration::get('PS_LANG_DEFAULT'), 'notification', $atos->l('Atos notification'), array('message' => 'Order: '.$result_array[22].' / '.$message), Configuration::get('ATOS_NOTIFICATION_EMAIL'), null, null, null, null, null, dirname(__FILE__).'/../../mails/'); } } } } // postProcess } // class |
/modules/atos/views/templates/front/validation.tpl :
Un thème Smarty doit être accouplé au contrôleur, même s’il ne sert à rien. Chez moi j’ai créé un fichier d’une seule ligne et deux octets qui contient juste : « ok ». Ce fichier est indispensable en raison de la ligne $this->template = _PS_MODULE_DIR_.'atos/views/templates/front/validation.tpl'; que nous venons d’ajouter au contrôleur /modules/atos/controllers/front/validation.php . Et cette ligne est indispensable au risque de se retrouver avec l’erreur suivante :
1 2 |
2019/08/20 09:44:15 [error] 10591#10591: *968 FastCGI sent in stderr: "PHP message: PHP Fatal error: Uncaught --> Smarty: 0():Missing '$template' parameter <-- thrown in /var/www/site/vendor/smarty/smarty/libs/sysplugins/smarty_internal_templatebase.php on line 177" while reading response header from upstream, client: 1.2.3.4, server: www.site.com, request: "GET /module/atos/validation HTTP/1.1", upstream: "fastcgi://127.0.0.1:9000", host: "www.site.com" |
/modules/atos/atos.php :
C’est ici que l’on modifie la valeur de l’URL d’auto-validation Atos (aussi appelée IPN) pour passer de https://www.site.com/modules/atos/validation.php à https://www.site.com/index.php?fc=module&module=atos&controller=validation.
1 2 |
// $ipn_page = $ps_silent_url.'modules/'.$this->name.'/validation.php'; $ipn_page = $ps_silent_url.'index.php?fc=module&module='.$this->name.'&controller=validation'; |
On pourrait envisager de s’en passer avec une réécriture d’URL gérée par mod_rewrite avec Apache ou la ligne suivante avec nginx.
1 |
rewrite "^/modules/atos/validation.php$" /index.php?fc=module&module=atos&controller=validation last; |
Je préfère néanmoins configurer la bonne URL de validation sans jouer avec les redirections d’URL de rustines.
La table ps_currency
Enfin, il n’est pas idiot de vérifier que la table ps_currency est cohérente dans la base de données car une donnée manquante (code iso, iso numérique) ou une précision à 6 peuvent causer des bugs (source).
Conclusion
L’utilisation des modules de paiement CB Atos est malheureusement un passage obligé pour bon nombre d’entre nous. Il est dommage que ces modules critiques ne soient pas supervisés par l’équipe de développement de Prestashop pour garantir un socle de fonctionnalités minimales à chaque mise à jour.
Cela n’est pas ma première déception avec le module Prestashop d’Atos. Il y a 2 ans je vous faisais part de leur incompatibilité avec HTTPS pour la validation des paiements. De la part d’Atos, il ne serait pas idiot de refondre totalement ce module avec un vrai chef de projet de développement web, qui inculque sa ligne de conduite avec du code propre, plutôt que d’empiler les rustines au fil des mises à jour.
En attendant on remet ça à plus tard, car tant que les ventes tombent, nul besoin de chercher plus loin 🙂
bonjour,
et merci beaucoup pour cette correction qui fonctionne et me tire une grosse épine du pied. En effet la version 5.0.3 d’Atos ne fonctionne pas correctement chez moi avec Prestashop 1.7.6.4. J’ai donc utilisé votre correction sur Atos 5.0.1.
Une question cependant: j’ai essayé de faire réapparaître l’affichage tout simple « Votre commande est validée ». Comme le hook displayOrderConfirmation est en partie géré par atos.php, j’ai essayé en rétablissant l’include de ce fichier. Mais cela ne suffit pas. Auriez-vous une idée pour le rétablissement de cet affichage? (Vous pouvez aussi me proposer un devis).
En vous remerciant,
bien cordialement
Bonjour,
j’ai finalement réussi à résoudre mon problème comme expliqué ici https://www.prestashop.com/forums/topic/1018613-r%C3%A9solu-atos-confirmation-de-commande/
Il n’empêche que j’ai apprécié votre réponse rapide (et raisonnable) alors merci pour cela (ainsi que pour la solution proposée ici) et je garde vos coordonnées…
Bien cordialement.
Bonjour, si vous avez trouvé une solution c’est parfait. Au plaisir de collaborer peut-être un jour ensemble !
Bonjour,
Votre solution a marché pour ma part !
Je vous remercie et reviens vers vous en cas de nouveau soucis.
Bonne journée.
Can you tell me where I can add this code?
Bonjour,
Je suis un peu perdu sur les étapes à suivre. Le mode de paiement via Atos ne s’affiche pas sur le front.
Merci de votre aide
Si le mode de paiement Atos ne s’affiche pas sur le front, vous n’êtes pas dans le cas de figure solutionné par cet article.
Cordialement
Bonne analyse et bonne solution ! Fonctionne parfaitement sur Prestashop 1.7.8.1.
Merci pour ce partage qui m’a fait gagner du temps !