1.5 1.6 1.7 1.7.7 1.7.8 8.1 +
N'hésitez pas à me le signaler si nécessaire via le formulaire de contact.
J’ai récemment du faire des tests de commandes via les Api de Prestashop et je n’ai pas trouvé de script tout fait qui le permettait.
Celui-ci utilise la librairie fournie par Prestashop et disponible sur github : https://github.com/PrestaShop/PrestaShop-webservice-lib/blob/master/PSWebServiceLibrary.php
En voici donc un basique qui va effectuer les actions suivantes :
- Récupération de l’identifiant client ( création du client si nécessaire )
- Récupération de l’identifiant de l’adresse du client ( création si nécessaire )
- Création d’un panier
- Passage de la commande
Ce script a été exécuté avec succès sur la version 1.7.3.3 de Prestashop et doit donc être compatible avec les versions suivantes.
Je n’ai pas constaté de changement fondamentaux dans l’api par rapport à Prestashop 1.6, pour lequel il devrait également fonctionner ( en changeant les produits )
N’hésitez pas à partager vos retours d’expériences sur l’utilisation de l’api de Prestashop
require_once('./PSWebServiceLibrary.php'); try { $host = 'https://yourshop.com'; $apiKey = 'APIKEY'; $webService = new PrestaShopWebservice($host, $apiKey, false); /** * On stocke ici les variables communes aux commandes créés via l'api */ $customerEmail = '[email protected]'; //Email du client ( A dynamiser dans le cadre d'une utilisation complète ) $carrierName = 'My carrier'; //Nom du transporteur dans prestashop ( utilisé pour récupérer son identifiant ) $paymentLabel = 'Paiement par chèque'; // Nom du mode de paiement $paymentCode = 'ps_checkpayment'; //Module de paiement utilisé /** * Liste des produits qu'on souhaite ajouter au panier */ $products = [ [ 'reference' => 'demo_12', 'qty' => 3, ], [ 'reference' => 'demo_19', 'qty' => 1, ], [ 'reference' => 'demo_6', 'qty' => 2, 'combination' => [ 'reference' => 'demo_6' ] ], ]; //Paramètres du client $customerDatas = [ 'firstname' => 'herve', 'lastname' => 'test', 'email' => '[email protected]', 'passwd' => 'mypassword', 'note' => 'Customer created with api', ]; //Paramètres de l'adresse $addressDatas = [ 'alias' => 'addresse api', 'id_customer' => $customerId, 'firstname' => 'herve', 'lastname' => 'test', 'address1' => 'rue des tests', 'address2' => 'encore rue des tests', 'postcode' => '67000', 'city' => 'strasbourg', 'phone' => '063656565', 'id_country' => 8, ]; //On regarde si le client existe $searchCustomerXml = $webService->get([ 'resource' => 'customers', 'filter' => ['email' => $customerEmail], ]); //Si il existe on récupère l'identifiant if (!empty($searchCustomerXml->children()->children())) { $customerId = (int)$searchCustomerXml->children()->children()[0]->attributes()['id'][0]; } //Si il n'existe pas on le créé else { //Création d'un client $clientXml = $webService->get(['url' => $host . 'api/customers?schema=blank']); foreach ($clientXml->customer[0] as $nodeKey => $node) { if (array_key_exists($nodeKey, $customerDatas)) { $clientXml->children()[0]->$nodeKey = $customerDatas[$nodeKey]; } } $opt = array('resource' => 'customers'); $opt['postXml'] = $clientXml->asXML(); $resultXml = $webService->add($opt); $customerId = (int)$resultXml->children()[0]->id; } //Récupération des adresses on part du principe qu'on récupère la première adresse du client si elle existe //On regarde si le client existe $searchAddressXml = $webService->get([ 'resource' => 'addresses', 'filter' => ['id_customer' => $customerId], ]); //Si il existe on récupère l'identifiant de la première adresse if (!empty($searchAddressXml->children()->children())) { $addressId = (int)$searchAddressXml->children()->children()[0]->attributes()['id'][0]; } //Si il n'existe pas on le créé else { //Création d'un client $addressXml = $webService->get(['url' => $host . 'api/addresses?schema=blank']); foreach ($addressXml->address[0] as $nodeKey => $node) { if (array_key_exists($nodeKey, $addressDatas)) { $addressXml->children()[0]->$nodeKey = $addressDatas[$nodeKey]; } } $opt = array('resource' => 'addresses'); $opt['postXml'] = $addressXml->asXML(); $resultXml = $webService->add($opt); $addressId = (int)$resultXml->children()[0]->id; } //Création d'un panier $cartXml = $webService->get(['url' => $host . 'api/carts?schema=blank']); //Définition des paramètres par défaut du panier //Tout ces paramètres pourront être récupérés de l'api en une fois et stockés $cartXml->cart->id_customer = $customerId; $cartXml->cart->id_address_delivery = $addressId; $cartXml->cart->id_address_invoice = $addressId; $cartXml->cart->id_currency = 1; //@Todo récupérer via api $cartXml->cart->id_lang = 1; //@Todo récupérer via api $cartXml->cart->id_shop = 1; //@Todo récupérer via api $cartXml->cart->id_shop_group = 1; //Identifiant du transporteur à récupérer dynamiquement ( peut bouger en étant édité dans le backoffice ) $carrierXml = $webService->get(['resource' => 'carriers', 'filter' => ['name' => $carrierName]]); if (!empty($carrierXml->children()->children()[0]->attributes()['id'][0])) { $id_carrier = (int)$carrierXml->children()->children()[0]->attributes()['id'][0]; } //Si il n'existe pas on le créé else { exit('Mode de livraison existe pas'); } $cartXml->cart->id_carrier = $id_carrier; //Récupération de identifiants des produits $productIds = array(); foreach ($products as $cartProduct) { $productSearchXml = $webService->get([ 'resource' => 'products', 'filter' => ['reference' => $cartProduct['reference']], ]); if (!empty($productSearchXml->children()->children()[0]->attributes()['id'][0])) { $id_product = (int)$productSearchXml->children()->children()[0]->attributes()['id'][0]; } else { //Si pas de produit on ne peut pas continuer continue; } if (isset($cartProduct['combination'])) { $combinationSearchXml = $webService->get([ 'resource' => 'combinations', 'filter' => ['reference' => $cartProduct['reference']], ]); if (!empty($combinationSearchXml->children()->children()[0]->attributes()['id'][0])) { $id_product_attribute = (int)$combinationSearchXml->children()->children()[0]->attributes()['id'][0]; } else { $id_product_attribute = 0; } } else { $id_product_attribute = 0; } $productIds[] = [ 'id_product' => $id_product, 'id_product_attribute' => $id_product_attribute, 'qty' => $cartProduct['qty'] ]; } //Insertion des lignes de produits foreach ($productIds as $productId) { $child = $cartXml->cart->associations->cart_rows->addChild('cart_row'); $child->id_product = $productId['id_product']; $child->id_product_attribute = $productId['id_product_attribute']; $child->quantity = $productId['qty']; $child->id_adddress_delivery = $addressId; } //Création du panier $opt = array('resource' => 'carts'); $opt['postXml'] = $cartXml->asXML(); $addCartXml = $webService->add($opt); //Création d'une commande ( à faire ) $orderXml = $webService->get(['url' => $host . 'api/orders?schema=blank']); //On défini les éléments de la commande spécifiques $orderXml->order->valid = 1; $orderXml->order->module = 'ps_checkpayment'; //Module de paiement $orderXml->order->payment = 'Paiement par cheque'; //Libéllé du mode de paiment //Les éléments communs avec le panier sont récupérés depuis celui-ci $orderXml->order->id_address_delivery = $addCartXml->cart->id_address_delivery; $orderXml->order->id_address_invoice = $addCartXml->cart->id_address_invoice; $orderXml->order->id_cart = $addCartXml->cart->id; $orderXml->order->id_customer = $addCartXml->cart->id_customer; $orderXml->order->id_carrier = $addCartXml->cart->id_carrier; $orderXml->order->id_currency = $addCartXml->cart->id_currency; $orderXml->order->id_shop = $addCartXml->cart->id_shop; $orderXml->order->id_shop_group = $addCartXml->cart->id_shop_group; $orderXml->order->id_lang = $addCartXml->cart->id_lang; // les lignes des produits sont aussi récupérées depuis le panier $orderTotal = 0; //Variable pour calculer le total de la commande unset($orderXml->order->associations->order_rows->children()[0]); //Suppression de ligne order_detail de démo foreach ( $addCartXml->cart->associations->cart_rows->cart_row as $cartRow) { //On ne traite pas les lignes vides if ( $cartRow->id_product == 0 || $cartRow->id_product =="") continue; //On récupère les informations disponibles depuis le panier $orderRow = $orderXml->order->associations->order_rows->addChild('order_row'); $orderRow->product_id = $cartRow->id_product; $orderRow->product_attribute_id = $cartRow->id_product_attribute; $orderRow->product_quantity = $cartRow->quantity; //Pour le reste on le récupère depuis les produits $productXml = $webService->get(['url' => $host . 'api/products/'.$cartRow->id_product]); $orderRow->product_name = $productXml->product->name; $orderRow->product_reference = $productXml->product->reference; $orderRow->product_price = $productXml->product->price; //Ajout du montant du produit au total du panier ( Le prix HT * la qté * 1+ Taux de tva ) $orderTotal += ( (float)$productXml->product->price * (int)$cartRow->quantity ) * 1.20 ; } //Données spécifiques à la commande $orderXml->order->total_paid_real = $orderTotal; //Montant payé réellement $orderXml->order->total_paid = $orderTotal; //Montant payé $orderXml->order->total_products = 1; //Montant total produit ht ( obligatoire mais indicatif car recalculé automatiquement ) $orderXml->order->total_products_wt = 1; //Montant total produit ttc ( obligatoire mais indicatif car recalculé automatiquement ) $orderXml->order->conversion_rate = 1; // Taux de conversion //Création de la commande $opt = array('resource' => 'orders'); $opt['postXml'] = $orderXml->asXML(); $addOrderXml = $webService->add($opt); echo 'Création de la commande '.$addOrderXml->order->id; } catch (PrestaShopWebserviceException $e) { echo $e->getMessage(); } |
Des optimisations et des factorisations sont encore possibles mais cela donne déjà une bonne base fonctionnelle 🙂
Bonjour,
Votre script fonctionne-il sur Prestashop 1.6 ?
Bonjour,
Je n’ai pas eut le temps de le tester, mais je pense qu’il doit fonctionner.
Les changements sur l’api ont été assez minimes entre les 2 versions.
( Les produits de l’exemples sont ceux de la version demo de la version 1.7 )
Cordialement,
Merci pour ce tuto, pour moi sa marche sauf que les frais de livraisons il ne sont pas inclus dans le calcule. et si on devais les ajouter je me demande si sa ne doit être qu’au niveau de l’ajout du Order seulement ou bien d’autres paramètres doivent aussi être crée . ?
Bonjour Kacem,
Votre question est intéressante.
J’ai regardé en détail le fonctionnement de la fonction d’ajout d’une commande et il s’avère qu’il n’est pas possible de gérer le montant des frais de livraisons via l’api. ( Du moins pas sans surcharge )
La fonction addWs de la classe Order ( qui créé une commande via api appelle la fonction PaymentModuleCore::validateOrder)
Dans celle-ci les montants de livraison et les totaux sont calculés automatiquement à partir des informations du panier.
Cordialement,
Hervé
Bonjour,
j’essaye de mettre en place un ws pour la création de commande. J’ai suivi le tuto mais j’ai une erreur :
This call to PrestaShop Web Services failed and returned an HTTP status of 500. That means: Internal Server Error.
Le client est correctement créé.
Le panier est correctement créé.
La commande elle, n’est pas créé. Et j’ai vérifié les permissions sont toutes cochés. Une idée de ce qui peut coincer ou de comment débugger cela ?
Merci
Bonjour Mathieu,
Est-ce que si vous activez le mode debug sur votre instance le message est plus explicite ?
Quelle est la version de prestashop ?
Cordialement,
Hervé
Merci pour votre retour.
En fait, c’est en activant le mode débug que j’ai pu récupérer ce message.
J’indique « true » à PrestaShopWebservice
$webService = new PrestaShopWebservice($host, $apiKey, true);
La version de prestashop est la 1.6. Vous indiquez bien ne pas l’avoir tester sur cette version mais je pensais que cela serait proche tout de même et c’est frustrant de pouvoir créer client et panier et de buter sur la commande.
Je vais essayer de monter un prestashop de test en 1.7.6 pour vérifier sur cette version.
Bien à vous
Mathieu
Ah, si, sur le prestashop 1.6, j’ai quand même le retour des XML envoyé, si ca peut donner une piste ?
HTTP REQUEST HEADER
POST //api/orders HTTP/1.1
Authorization: Basic N0JUMlNCUTNGVFZVV1lLNzNLQkNNMlJNNUw0NlVFVUI6
Host: http://www.xxx.com
Accept: */*
Content-Length: 1559
Content-Type: application/x-www-form-urlencoded
HTTP RESPONSE HEADER
HTTP/1.1 500 Internal Server Error
Date: Wed, 14 Aug 2019 13:11:19 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: SERVERID105612=144060; path=/; max-age=900
Server: Apache
X-Powered-By: PHP/7.0
XML SENT
3657
3657
12890
1
1
4130
66
ps_checkpayment
1
1
1
Paiement par cheque
18
18
1
1
1
7403001235.000000
Bonjour,
La réponse étant échappée par wordpress je n’arrive pas à voir la réponse exacte.
Il faudrait voir en même temps si vous avez des logs d’erreurs sur votre serveur pour voir la cause de l’erreur.
Bonjour,
J’ai le même problème que Mathieu sous prestashop 1.6.1.3
Le client / panier sont OK
Mais l’order ne fonctionne pas :
This call to PrestaShop Web Services failed and returned an HTTP status of 500. That means: Internal Server Error
Je n’arrive pas a afficher plus d’informations sur l’erreur.
@mathieu as-tu réussi a corrigé ton problème ?
Bonjour,
J’avais mis se projet en pause mais j’y suis revenu il y a quelques jours…
Finalement cela fonctionne bien sur Prestashop 1.6 Il faut faire (très) attention a avoir les bonnes valeurs sinon le panier semble ok mais cela coince au niveau de l’order.
Il me reste une erreur tout de même sur la methode de livraison :
Quel que soit l’id_carrier que je passe dans le XML de l’order, lorsque la commande est créé sur prestashop il prend le transporteur indiqué par défaut.
Exemple : je passe l’id 77 qui est le transporteur avec frais de port gratuit et dans la commande il me force l’id 79 qui est le transporteur par défaut à 5.90 € :/
Si quelqu’un a une idée je suis preneur. Est ce que vous rencontrer le même problème sur Prestashop 1.7 ?
Merci
Mathieu
Salut,
Merci mille fois pour le temps précieux que ce partage me fait gagner.
Je suis sur une version 1.7.6.2, je vous fais un retour dès que cela fonctionne.
A très bientôt.
Gab.
Bonjour,
Merci pour votre script !
Malheureusement, celui-ci ne prend pas en compte les frais de port.
Dans la commande que je tente de passer via l’API, je dois recalculer le total de la commande pour les frais de port.
L’API demande un champ total_paid_real, qui n’est pas recalculé lors du processing de la commande, et qui est utilisé pour l’enregistrement du paiement de la commande.
Je me demande s’il s’agit d’un bug du webservice ou d’un comportement normal, mais pour le moment, je peux pas créer une commande 100% valide, celle-ci bascule automatiquement en ‘erreur de paiement’
Bonjour,
Désolé je n’ai pas eut l’occasion de repasser sur ce script depuis un moment.
Est-ce que vous avez trouver une solution/un contournement ?
Cordialement,
Hervé
Bonjour Hervé, et encore merci pour tout votre travail mis à disposition.
Je ne parviens pas à faire fonctionner ce script.
La clé est bien générée, toutes les permissions pour la clef sont générées.
Si je mets host https://monsite.com j’obtiens une « redirection 302 inattendue » et si je mets https://www.monsite.com j’obtiens une erreur 401 « Unauthorized ».
Dans le htaccess je vois qu’il y a une règle de redir /api/ vers webservices/dispatcher.php. Je ne sais pas si c’est ça qui bloque…
Dans la librairie webservice, dans les defaultParams j’ai ajouté les 2 lignes :
CURLOPT_SSL_VERIFYHOST => 0,
CURLOPT_SSL_VERIFYPEER => 0
au cas où ce serait le certificat SSL qui gêne, mais rien…
Avez-vous une idée ? Merci merci d’avance.
Renaud.
Bonjour Renaud,
Vers quelle page se fait la redirection 302 ?
Je n’ai encore jamais rencontré ce type d’erreur qui doit sans doute être lié à une configuration particulière sur votre serveur.
Il faudrait tenter en console une commande du type
curl -I https://[email protected]/api/ pour voir les entêtes de la requête.
Cordialement,
Hervé
Merci Hervé, voici les résultats :
HTTP/1.1 302 Found
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Keep-Alive: timeout=15
Date: Thu, 11 Jun 2020 14:52:05 GMT
Server: Apache
X-Powered-By: PHP/7.2.31
Location: https://www.vivrenaturellement.com/api?url=
Mais dans le htaccess à la racine de PS, il y a :
RewriteRule ^api(?:/(.*))?$ %{ENV:REWRITEBASE}webservice/dispatcher.php?url=$1 [QSA,L]
Je ne vois pas à quoi sert ce dispatcher.php
Re bonjour Hervé,
Je viens de voir que si je saisis cette url :
https://www.monsite.com/api/customers?ws_key=xxxxxxx j’ai bien le XML de ma liste clients qui s’affiche…alors pourquoi votre script ne fonctionne-t-il pas ???
Progression ! En ajoutant la ligne
RewriteRule .* – [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] dans le htaccess et en mettant le www dans $host ça connecte ! Sauf que le débug dit BAD HTTP RESPONSE…
Dans la librairie je vois que :
if ($index === false && $curl_params[CURLOPT_CUSTOMREQUEST] != ‘HEAD’) {
throw new PrestaShopWebserviceException(‘Bad HTTP response’);
}
Donc je fais un print_r sur $curl_params et en effet je n’obtiens pas HEAD mais GET…
A devenir dingue, ce truc !
Bonjour Renaud,
C’est vraiment bizarre que vous ayez autant de problèmes avec cette api.
La lib prestashop existe depuis des années sans qu’elle pose spécialement de problèmes.
Dans un premier temps vous pouvez suivre la documentation officielle pour voir à quel niveau le problème se situe :
https://devdocs.prestashop.com/1.7/development/webservice/tutorials/prestashop-webservice-lib/
Bonjour Hervé, j’ai retourné cette doc dans tous les sens. Ce script de test fonctionne parfaitement :
require_once(‘./PSWebServiceLibrary.php’);
$host = ‘https://www.vivrenaturellement.com’;
$apiKey = ‘XXXXXXXXXXXXXXXXXXXX’;
try {
// creating webservice access
$webService = new PrestaShopWebservice($host, $apiKey, true);
// call to retrieve all customers
$xml = $webService->get([‘resource’ => ‘customers’]);
} catch (PrestaShopWebserviceException $ex) {
// Shows a message related to the error
echo ‘Other error: ‘ . $ex->getMessage();
}
Mais quand je tente d’exécuter votre script: Bad HTTP response
La loose…
Je pense que dans ce cas il faudra exécuter en step by step les différentes étapes.
C’est peut être une truc tout bête dans le copier/coller ou un élément spécifique à votre site.
Les différents commentaires à cet article semblent confirmer le bon fonctionnement global du script je ne sais donc pas trop vous préciser quoi faire de plus.
Merci, Hervé, je fais ça: step by step.
Par contre, dans votre script, il n’y a pas d’entrée dans order_history c’est normal ?
…je vois aussi que dans votre script vous n’implémentez pas order->current_state…et du coup le current_state va être à 0 non ?
Désolé d’insister, j’essaie de comprendre.
Bonjour Renaud,
J’avoue que ce script date un peu je n’ai plus forcément l’ensemble des processus en tête.
Il faudrait que je reste l’ensemble mais j’ai assez peu de temps pour le faire en ce moment.
Bonjour, avez vous réussi a ajouter le script définissant le statut de la commande ? j’essaie également de le faire mais je n’y parviens pas ! Merci d’avance pour votre retour
Hello guys, ou je peux trouver le fichier PSWebServiceLibrary.php ?
Bonjour,
La librairie est disponible ici : https://github.com/PrestaShop/PrestaShop-webservice-lib 😉