NF13075 — Interface de programmation applicative par Webservice (API)

De Documentation Polaris
Aller à : navigation, rechercher

Voir la carte de la fonctionnalité : Autres outils

L'API par Webservice vous permet de piloter ou d'interroger votre solution de gestion depuis un programme externe : c'est par son intermédiaire que le prestataire tiers choisi va mettre en place l'interconnexion à votre backoffice. Cette documentation lui est donc principalement destinée.

En effet, une interface de programmation applicative (désignée par le terme API pour Application Programming Interface) est un ensemble normalisé de classes, de méthodes ou de fonctions qui sert de façade par laquelle un logiciel offre des services à d'autres logiciels.

Dans le cas de Polaris, elle est offerte par un service web, accessible sur chaque service de réplication.

Choix techniques

Pour commencer

Notre API est bâtie sur l'exploitation d'un webservice, donc sur un échange HTTP pour lequel nous respectons le standard 1.1. Elle est compatible notamment avec les échanges sécurisés (SSL).

Pour échanger avec notre webservice, il faut donc le faire à travers un client HTTP traditionnel en manipulant des URI.

L'API est ouverte et accessible sur l'URL suivante : https://xx.xx.xx.xx:3443/core/api/.

SSL et certificat auto-signé

La plupart des marchands utilisant Polaris se servent d'un certificat auto-signé, souvent périmé.

La vérification automatique de la négociation SSL échouera donc très souvent. C'est pourquoi, lors de l'utilisation des API, vous devez gérer la connexion et la validation manuelle de ces certificats, en proposant, par exemple, de fixer l'empreinte du certificat lors de la première connexion avec les identifiants et n'accepter de vous connecter qu'à ce certificat quand le jeu de mot de passe correspond est utilisé.


Méthodes d'accès à l'API

Toutes les fonctions exposées par l'API sont assimilées à des ressources HTTP traditionnelle, le nom de la fonction est donc le nom de la ressource ;

Vous pouvez déclencher la fonction en appelant la ressource soit :

  • en méthode HTTP GET, les paramètres sont transmis en queryString ;
  • en méthode HTTP POST, chaque paramètre est transmis soit dans la queryString, soit dans le corps de la requête, au format application/x-www-form-urlencoded.


Notons qu'il ne s'agit ni d'un webservice de type SOAP, ni d'un service de type REST, par conséquent, les méthodes HTTP PUT, DELETE, et HEAD ne sont pas supportées.

Par exemple, la requête suivante :

GET /core/api/ACL.Login?user=test&password=testmdp

exécute la fonction ACL.Login, avec pour paramètres :

  • user avec la valeur test,
  • password avec la valeur testmdp.


Important !
Les valeurs étant passées dans la queryString, il faut nécessairement qu'elles soient encodées au format URL, notamment si elle contiennent le séparateur de champs (&) : ainsi, la valeur «test & autre» devient «test%20%26%20autre»

Gestion des réponses

A moins que le contraire ne soit clairement explicité dans leur documentation, comme par exemple l'API qui fourni les photos des produits, par défaut toutes les fonctions peuvent fournir leur réponses au format JSON ou XML, au choix lors de l'interrogation. C'est également valable pour les erreurs standardisées.

Exemple :

GET /core/api/Health.ProcessList?output=xml
<ArrayOfProcessInfo>
  <ProcessInfo>
    <Id>15</Id>
    <Name>ips</Name>
    <OwnerType>0</OwnerType>
    <Owner/>
    <OwnerName/>
    <Tasks>
      <ProcessInfoTask>
        <Date>2017-09-29T13:44:58.3608012Z</Date>
        <TaskName>Attente</TaskName>
      </ProcessInfoTask>
    </Tasks>
  </ProcessInfo>
</ArrayOfProcessInfo>
GET /core/api/Health.ProcessList?output=json
[
  {
   "Id":15,
   "Name":"ips",
   "OwnerType":0,
   "Owner":"",
   "OwnerName":"",
   "RemoteEndPoint":null,
   "Tasks":[
     {
      "Date":"\/Date(1506692818379)\/",
      "TaskName":"Attente",
      "Step":null
     }
    ]
  }
]

Vous pouvez également préciser que vous souhaitez une réponse au format JSON en le précisant dans l'entête "Accept" de la requête HTTP plutôt que dans la queryString :

  Accept: application/json

Gestion des erreurs

Il existe deux types d'erreurs, les standardisées, et les erreurs de transport.

Dans les deux cas les erreurs sont propagées par le système de code d'état de la requête (les erreurs sont propagées par le système de code d'erreur traditionnel du protocole HTTP) :

  • le code d'erreur 200 signifie que la fonction s'est éxécutée correctement,
  • le code 301 et 302 désignent que la fonction a été déplacée,
  • le code 404 désigne une fonction qui n'existe pas ou plus,
  • les codes 401 et 403 désignent un manque de droits pour l'éxécution,
  • et enfin le code 500 désigne une erreur standardisée, décrite ci-dessous.

Tous les autres codes d'erreurs HTTP sont dépendants soit d'une erreur dans la formulation de la requête HTTP (code 4xx), soit d'une erreur ponctuelle non liée à la fonction (5xx).

Lors d'une erreur 500, vous pouvez récupérer l'erreur standardisée qui retrace l'exception sur notre système et le problème via le corps de la réponse. Comme vu précédemment, en fonction de la requête (Accept / QueryString &output=json), cette erreur est décrite en XML ou en JSON.

Par exemple :

GET /core/api/ACL.TraitmentCancel
<PHCAPIError>
  <Error>0</Error>
  <StackTrace>
   à WebServiceCore.API.HttpComponentAPIManager.<>c__DisplayClass28_3.<__BuildResources>b__0(HttpResponse lnk) dans C:\Work\Polaris\dev\sources\WebServiceCore\WebServiceCore\API\HttpComponentAPIManager.cs:ligne 958
  </StackTrace>
  <Message>paramètre 'traitmentName' manquant</Message>
</PHCAPIError>

Gestion de l'identification et des droits d'accès

L'identification est propagée par l'ouverture et la transmission d'un cookie qui contient un jeton de session.

Afin de pouvoir utiliser correctement l'API, vous devez :

  1. appeler la fonction ACL.SSL pour vous voir redirigé sur un port sécurisé si ce n'est pas le cas ; à partir de ce point, toute la communication est protégée ;
  2. toujours propager les cookies de session reçus avec le client HTTP que vous utilisez pour forger et envoyer vos requêtes,
  3. ouvrir une session sur le système en utilisant la fonction de connexion ACL.Login au début de l'échange,
  4. fermer votre session en utilisant la fonction de connexion ACL.Logout à la fin de votre échange.

Le jeton de session fourni est valable au moins 20 minutes entre chaque appel de fonctions.

Exemple

<?php
   /*
       Exemple de connexion à Polaris API
       Cet exemple montre comment se connecter aux webservice /core/api avec CURL

       Pour cet exemple, nous allons :
        * nous connecter au service
        * propager les cookies
        * lister les deux premières pages des produits du système

       ---

       Connection to Polaris API v1 example
       This example show how to connect with the Polaris /core/api webservice with CURL

       For this example, we will :
        * connect to the service
        * propagate cookies
        * list first 2 pages of products on the system

       (c) VEGA Informatique
   */    

   // La configuration de l'exemple est après l'implémentation de la classe
   //
   // Note : pour renforcer la sécurité, vous pouvez n'accepter qu'un certificat bien précis, peu importe qu'il soit valide ou non
   // cf. https://curl.haxx.se/libcurl/c/CURLOPT_PINNEDPUBLICKEY.html
   // ---
   //
   // See example configuration below, after class implementation
   //
   // Note : to increase security, you can modify this example and do certificate pinning to refuse all valid certificate YOU don't trust
   // cf. https://curl.haxx.se/libcurl/c/CURLOPT_PINNEDPUBLICKEY.html
   
   // ------------------------------------- CONFIGURATION ---------------------------------------
   class VegaExample {
       // URL et port du service, + identifiants
       // URL and port of service + user/pass
       private $polarisUrl;
       private $polarisUser;
       private $polarisPass;
   
       // Magasin de certificats autorisés
       // Self-signed store
       private $certSelfSignedStore = null;
       private $acceptCertificate = false;
       
       private $curlHandle = null;

       /*
           Déclare un nouvel exemple
               url : url du service, sans chemin
               user/pass
               selfSignedCert : vrai pour gérer les certificats auto-signés
               acceptCertificate : accepte le certificat présenté et sauvegarde de ce dernier dans le magasin de certificats

           New example
               url : URL of service, no path
               user/pass
               selfSignedCert : turn on to handle self-signed certificate managing
               acceptCertificate : accept all new certificate (Keep at false, except to register self-signed certificate)
       */
       function __construct($url, $user, $pass, $selfSignedCert = true, $acceptCertificate = false)
       {
           $this->polarisUrl = $url;
           $this->polarisUser = $user;
           $this->polarisPass = $pass;   
           $this->certSelfSignedStore = $selfSignedCert ? getcwd() . "/server.ca" : null; 
           $this->acceptCertificate = $acceptCertificate;
       }

       /*
           Identification
           Authentication
       */
       function auth()
       {            
           $ch = curl_init();

           // common options
           curl_setopt_array($ch, [
                               CURLOPT_CUSTOMREQUEST => "GET",
                               CURLOPT_HEADER => 0,
                               CURLOPT_USERAGENT => "Example/1.0",            
                               CURLOPT_FRESH_CONNECT => 0, 
                               CURLOPT_FORBID_REUSE => 0,
                               CURLOPT_RETURNTRANSFER => 1, 
                               CURLOPT_CERTINFO => 0,
                               CURLOPT_FOLLOWLOCATION => 1,
                               //CURLOPT_TCP_NODELAY = 1,
                               //CURLOPT_SSL_VERIFYSTATUS => 1,
                               CURLOPT_SSL_VERIFYPEER => 1,
                               CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1                                
                           ]);
           
           /*
               STEP 1 : 
                   Redirection SSL si nécessaire
                   Get redirected to SSL if necessary
               --------
           */
           if (substr($this->polarisUrl, 0, 5) === "http:")
           {
               curl_setopt($ch, CURLOPT_URL, $this->polarisUrl . "/core/api/ACL.SSL?onlyGetAddress=true");
               
               // Execute
               if( ! $result = curl_exec($ch))
                   trigger_error(curl_error($ch));
               if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != 200)
                   throw new \Exception("Error while getting SSL redirection URL !");

               // A ce stade, $result contient une URL vers la version sécurisée du service, nous pouvons remplacer notre URL par celle-ci
               // On this step, $result contains the fully secured URL. 
               // we can replace our own URL 
               // Ex. : http://pl-vega-0001.vega-net.net:3600/core/api/ACL.SSL -> https://pl-vega-0001.vega-net.net:4043/core/api/ACL.SSL        
               $this->polarisUrl = substr($result, 0, strlen($result) - strlen("/core/api/ACL.SSL"));

               // Ok, le prochain appel sera donc fait quoiqu'il arrive en SSL
               // Ok, next call will be done on SSL ...
           }

           /*
               STEP 2 : 
                   Si nous utilisons les certificats auto-signé, peut-être avons nous à le télécharger
                   If using self-signed cert, check if we need to download it ...
               --------
           */
           if ($this->certSelfSignedStore)
           {
               if (!file_exists($this->certSelfSignedStore))
               {
                   // Obtenir le certificat
                   // Get certificate
                   curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
                   curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
                   curl_setopt($ch, CURLOPT_CERTINFO, 1);
                   
                   // on appel n'importe quelle API qui ne nécessite pas d'identification
                   // so calling any endpoint, let's try ACL.WhoAmI
                   curl_setopt($ch, CURLOPT_URL, $this->polarisUrl . "/core/api/ACL.WhoAmI");

                   $result = curl_exec($ch);            
                   if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != 200)
                       throw new \Exception("Error while certificate downloading !");

                   $certs = curl_getinfo($ch, CURLINFO_CERTINFO);
                   if (!$certs)
                       throw new \Exception("No certificate found");

                   if ($this->acceptCertificate)
                   {
                       // On sauve le certificat dans le magasin
                       // Saving certificate in CA file
                       $certData = "";
                       foreach ($certs as $certificate)
                           $certData .= $certificate["Cert"];

                       if (!file_put_contents($this->certSelfSignedStore, $certData))
                           throw new \Exception(sprintf("can't create %s", $this->certSelfSignedStore));
                   }
                   else
                   {
                       // On affiche seulement son empreinte
                       // only display cert finger print
                       $msg = "Unknown self-signed certificat chain with fingerprints : \n";
                       foreach ($certs as $certificate)
                           $msg .= openssl_x509_fingerprint($certificate["Cert"], "sha256") ."\n";

                       $msg .= "\n";
                       $msg .= "To trust it, rerun once with 'acceptCertificate' to true";
                       throw new \Exception($msg);
                   }

                   curl_setopt($ch, CURLOPT_CERTINFO, 0);
               }

               // Initialisation du magasin / Setup
               curl_setopt($ch, CURLOPT_CAINFO, $this->certSelfSignedStore);
               // Nous faisons confiance à ce certificat
               // We are trusting this certificate, so 
               curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
               // Il peut avoir été émis avec un nom différent, peu importe
               // It can be emitted for a different name, no matter
               curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
               // Il peut être révoqué, peu importe
               // It can be revoked, no matter
               curl_setopt($ch, CURLOPT_SSL_VERIFYSTATUS, 0);
           }
           else
           {
               // Initialisation SSL : ici, nous n'acceptons que les certificats valides
               // Setup : we are not trusting self-certificate, so it must check all security steps
               curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
               curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 1);
               curl_setopt($ch, CURLOPT_SSL_VERIFYSTATUS, 1);
           }

           /*
               STEP 3 : 
                   Connexion
                   Login
               --------
           */
           // Prise en charge des cookies / Manage cookies
           curl_setopt($ch, CURLOPT_COOKIEJAR, getcwd(). "/session.cookie");
           curl_setopt($ch, CURLOPT_COOKIEFILE, getcwd(). "/session.cookie");
           curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
           curl_setopt($ch, CURLOPT_URL, $this->polarisUrl . sprintf("/core/api/ACL.Login?user=%s&password=%s", 
                                       urlencode($this->polarisUser), 
                                       urlencode($this->polarisPass)));

           if( ! $result = @curl_exec($ch))
               trigger_error(curl_error($ch));
           
           if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != 200)
               throw new \Exception("Error on login !");

           $this->curlHandle = $ch;
       }

       /*
           Déconnexion et libération de ressources sur le serveur
           Logout and free resource on server
       */
       function logout()
       {
           $ch = $this->curlHandle;
           if (!$ch)
               return; // déjà déconnecté / already logued out !
           
           curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
           curl_setopt($ch, CURLOPT_URL, $this->polarisUrl . "/core/api/ACL.Logout");

           $result = curl_exec($ch);
           curl_close($ch);

           $this->curlHandle = null;
       }

       /*
           Obtenir la liste des produits
           Get product list
       */
       function getProductList($page = 1, $pageSize = 100)
       {
           $ch = $this->curlHandle;
           if (!$ch)
               throw new \Exception("not connected!");

           curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
           curl_setopt($ch, CURLOPT_URL, $this->polarisUrl . sprintf("/core/api/Catalog.SearchProducts?output=json&pageSize=%d&pageNo=%d", $pageSize, $page));
           if( ! $result = curl_exec($ch))
               trigger_error(curl_error($ch));
           if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != 200)
               throw new \Exception("Error on getting products !");

           $data = json_decode($result);
           return $data->Items;
       }

       /*        
           Qui suis-je ?
           Who am I ?
       */
       function getWhoAmI()
       {
           $ch = $this->curlHandle;            
           if (!$ch)
               throw new \Exception("not connected!");

           curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "GET");
           curl_setopt($ch, CURLOPT_URL, $this->polarisUrl . "/core/api/ACL.WhoAmI");
           if( ! $result = curl_exec($ch))
               trigger_error(curl_error($ch));
           if (curl_getinfo($ch, CURLINFO_HTTP_CODE) != 200)
               throw new \Exception("Error on getting identity !");

           return $result;            
       }
   }

   /*
       EXAMPLE
   */

   // La première fois, passez à vrai pour accepter le certificat autosigné, ensuite, remettez à faux
   // Only the first time, turn on accept the unknown self-signed certificate
   // Then, turn off to not accept others one
   $acceptCertificate = false;

   $example = new VegaExample("https://82.64.49.122:13443", 
                               "webapi", 
                               "***", 
                               true, 
                               $acceptCertificate,
                           );
   $example->auth();
   
   // Qui suis-je ?
   // Display who am i ...
   var_dump($example->getWhoAmI());
   
   // Tirons les 200 premiers produits
   // Pull first 200 products ...
   $idx = 1;
   echo "+-------+---------------------------+-----------+\n";
   echo "|   idx |              product code | min price |\n";
   echo "+-------+---------------------------+-----------+\n";
   for ($page = 1; $page <= 2; $page++)
   {        
       foreach ($example->getProductList($page, 100) as $product)
           echo sprintf("| %5d | %25s | %8.2f€ |", $idx++, strtoupper($product->Code), $product->PriceTTC)."\n";        
   }
   echo "+-------+---------------------------+-----------+\n";

   $example->logout();


Liste des fonctions accessibles

La liste des fonctions accessibles est disponible ici : pages relatives au Webservice.