Accueil / Articles PiApplications. / Sujets autour de la programmation / Paiement en ligne via PayPal.

Intégration de l'API REST PayPal.

Le protocole REST (Representational State Transfer) est une dérivation du protocole HTTP qu'il utilise comme moyen de transport. Peu à peu, ce protocole est devenu un argument commercial traduisant la communication inter-systèmes via le protocole HTTP. Techniquement, l'ancien protocole REST est devenu HATEOAS (Hypermedia As The Engine of Application State), dénomination que vous trouverez dans la documentation PayPal.

Le grand intérêt de REST est de fonctionner de façon strictement identique au Web en utilisant des jeux de requêtes-réponse s'appuyant sur le protocole HTTP. Cela évite d'imposer au programme client (le navigateur ou l'application mobile) d'avoir à mettre en oeuvre un protocole dédié. C'est donc un protocole bien adapté au Web et à l'Internet.

Nous avons déjà abordé dans un article de présentation du paiement en ligne les solutions PayPal d'intégration d'un bouton de paiement ou d'envoi d'un courriel. Cet article ne traite aucun de ces deux mécanismes.

Toutes les autres solutions PayPal présentent techniquement la même souche : un dialogue entre votre application et un centre PayPal via échange de données au format JSON. Quelque soit le moyen et le protocole utilisés, vous adressez à PayPal un fichier JSON qui constitue une "requête" et vous recevez un flux au format JSON qui est la "réponse". Le protocole REST n'est finalement qu'une appellation du moyen de transport de ces fichiers. Il s'agit dans les faits d'un échange requête-réponse ordinaire. Au lieu de transporter un flux de type MIME "text/html", on transporte un flux de type "application/json". La requête utilise la méthode HTTP POST.

JSON est donc le format d'échange de données de la solution quelque soit le mécanisme mis en oeuvre. On peut réaliser cet échange via de nombreuses API comme PHP, C#, Ruby, cURL (si CGI), etc. PayPal propose même une librairie JavaScript comme API.

Pour notre part, nous allons utiliser la langage Java n'imposant aucun script. Alors qu'il s'agit d'une opération technique assez simple et robuste, plusieurs choses la complique :

Dans le cas de Java, cela se complique encore car vous devrez vous-mêmes compiler certaines librairies avec bien entendu un minimum de documentation à la clef. Disons le toute de suite, intégrer PayPal n'est pas forcément très intuitif même si le principe et les échanges sont simples en eux-mêmes.

En face de ses "contre", il y a heureusement des "pour". PayPal a visiblement mené une réflexion au profit des développeurs et lui consacre un site (voir liens en bas de page d'un site PayPal). Au-delà du site, PayPal met en oeuvre deux plates-formes :

  1. une plate-forme opérationnelle de production appelée live ;
  2. une plate-forme dédiée aux seuls tests sans lien avec la plate-forme précédente nommée sandbox.

Le site "développeur" permet à tout un chacun de créer un compte de développement sur la plate-forme sandbox. A partir de ce compte, vous pouvez créer des comptes PayPal fictifs de différents types et déclarer des applications. Les applications déclarées sont actives aussi bien pour la plate-forme opérationnelle que pour la plate-forme de test. Elles ont cependant des paramètres d'authentification (ClientID et Secret) différents.

Votre compte développeur dispose d'un "tableau de bord" qui vous résume les informations nécessaires à vos tests et affiche les transactions réalisées en test des 30 derniers jours. Vous pouvez ainsi tester autant de fois que vous le souhaitez vos applications et leurs scénarii avant de déployer votre solution sur votre plate-forme en production.

Préalables au développement.

Il y a des préalables fonctionnels et techniques avant la mise en oeuvre d'une paiement en ligne via PayPal.

Les préalables fonctionnels.

Comme nous l'avons déjà vu, vous devez en tout premier lieu posséder un compte PayPal acceptant de recevoir des paiements.

L'API REST impose des éléments de connexion propres à votre application Web dans l'étape initiale du workflow. Ces éléments sont nommés Client ID et Secret. Pour les obtenir, vous devez créer votre application via votre compte PayPal. Cette création met à votre disposition deux jeux de paramètres d'identification : un de test nommé "sandbox" et un réel nommé "live".

Vous ne devez jamais communiquer ces éléments (particulièrement les éléments opérationnels de connexion "live").

Ensuite, vous devez définir le ou les scénarii de workflow marchand que votre future application doit mettre en oeuvre. Cette étape est cruciale pour la robustesse même de votre système et donc sa sécurité.

Les préalables techniques.

PayPal fournit de nombreuses explications d'utilisation de l'API REST pour toutes sortes de plates-formes (.Net, PHP, script shell CGI, etc.) et toute sorte de workflows. Nous nous limiterons ici à la plate-forme Java (hors Android) et à un workflow élémentaire décrit plus loin.

PayPal a développé une librairie Java visant à faciliter l'utilisation de l'API REST dans le cadre de l'intégration de son moyen de paiement à un système pouvant utiliser des communications via le protocole HTTP. Cette librairie se nomme rest-api-sdk.jar et les sources sont accessibles via un site GIT (actuellement en version stable 1.2.10).

Il va donc falloir commencer par compiler ces sources car le site ne délivre pas à ce jour (novembre 2015) de binaires. L'organisation du projet permet d'utiliser directement le séquenceur de tâches Maven puisque le fichier pom.xml est fourni.

Pour notre part, nous avons téléchargé le fichier compressé des sources et avons légèrement transformé la structure pour en faire un projet NetBeans. Toutefois quel que soit l'outil utilisé, cela ne compilera pas avec les seuls sources récupérés. Il manque la librairie Google nommé GSON de traitement des flux JSON et la librairie d'interface d'historisation slf4J.jar. Cette dernière peut être téléchargée sans avoir à compiler les sources. Le sources de la librairie GSON sont également disponibles depuis un site GIT. Il peuvent être compilés directement (pas de référencement d'autres librairies) avec Maven (ou en tant que projet NetBeans après une légère adaptation de structure).

Ceci fait, vous devez référencer ces deux nouvelles librairies dans votre projet.

En résumé vous devez posséder à ce stade :

Intégration du moyen de paiement.

Nous voici enfin arrivé au coeur de l'intégration de la solution de paiement. PayPal offre de nombreux "schémas" de transaction financière voire commerciale. Nous en retenons un basic et très courant :

  1. présentation au client du récapitulatif de sa commande et d'un bouton de validation ;
  2. redirection du client vers le site PayPal pour approbation ou annulation du paiement ;
  3. exécution du paiement.

Au plan du test, la difficulté réside dans la rupture de charge entre votre site et celui de PayPal. Le schéma qui suit présente ce qui se passe dans le cadre du workflow simple décrit ici :

Synoptique du workflow.

Sur ce schéma, les pages en gris sont celles de votre application tandis que celles en bleu sont celles de PayPal. Les carrés orange sont les traitements qui réalisent les échanges de données entre votre application et PayPal. Dans un premier temps, ces échanges préparent la transaction financière (la fourniture au "guichet " des éléments préalables au paiement décrits dans la présentation du paiement en ligne). Dans un second temps, ils valident le paiement après qu'il ait été approuvé par votre client.

On peut s'interroger sur l'intérêt de la phase "d'exécution" du paiement. Pourquoi n'est-il pas exécuté après que le client ait validé son paiement sur le site PayPal ? Nous y voyons deux raisons :

  1. Cela protège le "client" si l'application "plante" lorsque PayPal invoque votre site en retour. En effet, le paiement n'ayant pas été exécuté, rien ne sera retiré sur le compte du client malgré son approbation.
  2. Cela permet au "marchand" d'être informé de l'approbation du client et lui assure la garantie du transfert du montant dû. Cette garantie autorise le déclenchement d'actions automatiques comme la mise en préparation sans délai de la commande voire sa livraison immédiate (biens immatériels).

Chaque carré orange sur le schéma représente un ou plusieurs échanges de fichiers JSON (JavaScript Object Notation. Ce format est un format d'échange de données qui a pris beaucoup d'importance ces dernières années car il est aussi utilisé par la plupart des moteurs de recherche pour obtenir les méta données de vos pages Web. Son écriture utilise des jeux d'accolades qui peuvent être imbriqués. Ce format ressemble a du code JavaScript et peut se placer comme lui dans une page Web via l'élément <scrip> d'où son nom mais là s'arrête l'analogie. Un jeu d'accolades définit en fait un groupe de propriétés et éventuellement de sous-groupes de propriétés voire des tableaux de groupes de propriétés pouvant eux-mêmes comporter des sous-groupes. Le format JSON définit un arbre dont chaque noeud peut comporter une liste de propriétés ou un autre arbre de même type. Malgré sa forme facilement lisible, il s'agit en fait d'une structure de données assez complexe à analyser. Dans le cas de l'API PayPal, c'est la librairie GSON (origine Google et libre de droits) qui est chargée de la désérialisation ou de la sérialisation des flux au format JSON.

L'API REST PayPal Java a deux rôles essentiels :

Notez que les racines des URL des centres PayPal de test et de production sont différents. Pour une plate-forme considérée, les URL des centres d'authentification de l'application et ceux des échanges liées à la transaction financières sont également différents.

Il est tout à fait possible d'écrire un programme Java console ou graphique pour vérifier, avant de les intégrer à votre application Web, le fonctionnement des principales méthodes de votre workflow sur la "sandbox".

En effet, une des difficultés du test est de reproduire sur la plate-forme de développement et de test le comportement de l'application opérationnelle. Vous devez indiquer à PayPal les URL des pages de retour en cas de validation ou d'abandon du processus de paiement par le client. Hors souvent, les plates-formes de test ou de développement ne sont pas reliées (pour des raisons de sécurité) directement à l'Internet. Il est possible de contourner cette difficulté en configurant certains éléments de réseau et/ou de sécurité. Par exemple, il est possible de placer la plate-forme de développement ou de test derrière un proxy "transparent" ou derrière un pare-feu qui effectue une translation d'adresse et/ou de port. Dans tous les cas, vous devrez configurer votre plate-forme pour que le serveur PayPal puisse d'une part réaliser les échanges de flux JSON et d'autre part invoquer les adresses de retour que vous lui indiquez.

Un exemple d'intégration.

Nous avons définit 3 étapes à notre workflow de préparation et d'exécution d'un paiement. Nous allons détailler chacun d'elles.

Initialisation du contexte du workflow.

Avant d'exécuter tout workflow, l'API PayPal a besoin de données techniques portant sur les paramètres de communication. Il s'agit de propriétés que l'on peut enregistrer dans un fichier de propriétés ou définir de manière dynamique. Le code ci-dessous monte ces propriétés :

private HashMap<String, String> buildPayPalSetup()
  {
    HashMap<String, String> hmp = new HashMap<>();
    hmp.put("http.ConnectionTimeOut", "5000");
    hmp.put("http.Retry", "1");
    hmp.put("http.ReadTimeOut", "30000");
    hmp.put("http.MaxConnection", "100");
    hmp.put("http.ProxyPort", "8080");
    hmp.put("http.ProxyHost", "127.0.0.1");
    hmp.put("http.UseProxy", "false");
    hmp.put("http.ProxyUserName", "");
    hmp.put("http.ProxyPassword", "");
    hmp.put("http.GoogleAppEngine", "false");
    hmp.put("service.EndPoint", "https://api.sandbox.paypal.com");
    return hmp;
  }

Bien évidemment, la propriété service.EndPoint devient https://api.paypal.com sur une plate-forme opérationnelle. Les propriétés sont assez triviales pour qu'il ne soit pas nécessaire de développer plus avant.

La première étape consiste à transmettre ces propriétés à l'API PayPal afin de demander au serveur d'authentification PayPal un jeton d'accès réservé à votre application Web après lui avoir fourni les éléments d'authentification :

private String initPayPal()
      throws PayPalRESTException
  {
    // Compte sandbox
    String sClientID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    String sSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
    HashMap<String, String> hmp = buildPayPalSetup();
    OAuthTokenCredential oat = new OAuthTokenCredential(sClientID, sSecret, hmp);
    return oat.getAccessToken();
  }

Bien évidemment, les 'x' sont à remplacer par vos propres éléments (sandbox ou live) selon le cas. Les éléments de connexion permettent au serveur PayPal d'identifier vore application et à partir d'elle votre compte sans qu'aucune donnée liée à ce dernier ne soit transportée (réduction très notable du risque en cas d'écoute "illicite"). PayPal retourne en réponse un jeton d'accès qui est dans les faits un jeton d'identification du workflow de votre application et qui sera réutilisé par la suite à la manière d'un marqueur de transaction Web.

Création du paiement à exécuter.

La seconde étape consiste à fournir à PayPal les éléments de la transaction financière qu'il devra proposer à votre client pour approbation. Ces éléments sont :

Voici un exemple de code qui réalise cette étape pour un montant de 20,10 € :

private Payment createPayment(String sAccessToken)
      throws PayPalRESTException
  {
    Amount amn = new Amount("EUR", "20.10");
    Transaction trn = new Transaction();
    trn.setDescription("Création d'un paiement de 10 € au profit de PiApplications en règlement de la commande N° 123456 du 18/11/2015.");
    trn.setAmount(amn);
    List<Transaction> lst = new ArrayList<>();
    lst.add(trn);
    Payer pyr = new Payer("paypal");          // Peut être 'credit_card'
    Payment pym = new Payment("sale", pyr);
    pym.setTransactions(lst);
    RedirectUrls rur = new RedirectUrls();
    rur.setCancelUrl("https://www.piapplications.eu/shop?result=ok");
    rur.setReturnUrl("https://www.piapplications.eu/shop?result=cancel");
    pym.setRedirectUrls(rur);
    APIContext acn = new APIContext(sAccessToken);
    acn.setConfigurationMap(buildPayPalSetup());
    return pym.create(acn);
  }

L'objet de classe Payment retourné est l'interprétation par l'API des données JSON reçues en réponse à la demande de préparation du paiement. En dehors de l'identifiant de transaction et de l'identifiant de paiement, le protocole HATEOAS fournit une liste de liens. Cette liste permet de savoir vers où rediriger l'utilisateur en fonction du workflow envisagé. Par exemple, pour obtenir simplement l'autorisation de paiement, il faudra rechercher dans les liens HATEOAScelui nommé approval_url. Voici un exemple de code qui effectue cette recherche sur la liste des liens reçus (JDK 1.8 avec utilisation de programmation fonctionnelle) :

private String getApprovalURL(List<Links> lst)
  {
    Optional<Links> opt = lst.stream().filter(lnk -> lnk.getRel().equals("approval_url")).findAny();
    if (!opt.isPresent())
      return null;
    return opt.get().getHref();
  }

Exécution du paiement.

Lorsque le serveur PayPal a reçu l'approbation du client, il invoque (via une méthode GET) l'URL que vous lui avez transmis précédemment en y ajoutant 3 paramètres payer_id, paymentId et token. Ce dernier paramètre n'est pas utilisé ici.

Muni des deux autres paramètres, vous pouvez alors exécuter la transaction ce qui l'achèvera. Notez que seul le paramètre payer_id dans notre cas doit être transmis en tant que donnée. Le paramètre paymentId participe à la construction de l'URL d'invocation du serveur PayPal.

Toutefois, avant de demander l'exécution du paiement, il est préférable d'authentifier de nouveau l'application. Voici un exemple de code de l'ensemble de ces opérations :

      // Récupération des paramètres HTTP retournés par PayPal
      HashMap<String, String> hmp = buildPayPalSetup(cfg);
      OAuthTokenCredential oat = new OAuthTokenCredential(cfg.getPayPalClientID(), cfg.getPayPalSecret(), hmp);
      String sTokenAccess = oat.getAccessToken();
      APIContext acn = new APIContext(sTokenAccess);
      acn.setConfigurationMap(hmp);
      String sPaymentID = hmpParams.get("paymentId");
      String sPayerID = hmpParams.get("PayerID");
      // Prépare les données à émettre
      Payment pym = new Payment();
      pym.setId(sPaymentID);
      PaymentExecution pex = new PaymentExecution(sPayerID);
      Payment pymExecuted = pym.execute(acn, pex);
      // Trace de l'exécution
      cfg.getLogger().functionnalInfo(MessageFormat.format(lcl.getString("ReadingPaymentInfo"),
          pymExecuted.getId(), pymExecuted.getState()));
      // Trace du paiement effectué
      Payer pyr = pymExecuted.getPayer();
      String sPaymentMethod = pyr.getPaymentMethod();  // credit_card ou paypal
      if (sPaymentMethod == null)
        sPaymentMethod = "?";
      ...
      // poursuivre avec actions automatiques liées à la réception du paiement
      ...

Dans cet exemple hmpParams représente la liste des paramètres HTTP reçus avec la requête. Notez que la transmission d'un objet de classe APIContext est ici obligatoire sans quoi, vous aurez très probablement une erreur de classe MalformedURLException (après dépilement de l'exception reçue).

Comme vous pouvez le constater dans cet exemple de code, à la fin de l'exécution, l'objet de classe Payment que vous obtenez en retour contient l'ensemble des données relatives à la transaction et à votre client.

Voilà, nous espérons avoir un peu démystifier l'intégration du paiement en ligne via PayPal qui reste malgré tout une opération dont la simplicité ou la complexité dépend essentiellement du workflow mis en oeuvre. Il est assez facile d'adapter ce code pour permettre la saisie des éléments de carte bancaire et les adresser à PayPal pour paiement. Nous ne recommandons toutefois pas cette approche si vous ne pouvez pas garantir la confidentialité des éléments bancaires saisis. La paiement que nous avons proposé en exemple ici n'impose pas à votre client un compte PayPal. La page que lui présente PayPal lui permet de régler par carte bancaire s'il le souhaite ou s'il n'a pas de compte PayPal.

Enfin, PayPal propose un assez grand nombre de services via ces échanges JSON (gestion d'un panier, des éléments d'une commande, etc.). Une fois ces principes de base acquis, cela peut ouvrir d'autres horizons.

(c) PiApplications 2016