MAJ – Créer dynamiquement un bouton Paypal sécurisé
Il existe une multitude de possibilités pour faire un bouton de paiement Paypal, la plus courante est de créer le bouton dans l’interface de celui-ci et de copier coller le code qu’il vous donne au final sur votre page. Le défaut de cette méthode c’est que l’on doit créer un bouton pour chaque objet que l’on vend, la flexibilité n’est donc pas au rendez vous avec cette méthode. La deuxième méthode utilisée est la possibilité de créer un formulaire avec les paramètres définis sur cette page. Avec cette méthode la flexibilité est présente mais pas la sécurité. En effet il est alors facile de modifier le prix du panier dans le formulaire avec l’extension Firebug de Firefox et ainsi falsifier le prix à payer… Si la cohérence des informations n’est pas vérifiée lors du feedback de Paypal (IPN) c’est un réel problème !
Je vais donc vous présenter la méthode permettant de générer le bouton Paypal avec un cryptage SSL des données du formulaire vous garantissant un paiement sécurisé de bout en bout… Attention tout de même cette méthode n’est pas à la portée de tout le monde car il y a certaines contraintes techniques.
Comme je le disais il existe des contraintes techniques et votre environnement doit répondre aux besoins suivants :
- Un accès à la fonction exec de PHP (exit si votre environnement est configuré en safe_mode à on) ou équivalent.
- Avoir un accès SSH en ligne de commande (Pour pouvoir générer les certificats SSL, même si je pense qu’ils peuvent être générés sur votre machine et envoyés ensuite sur votre serveur)
- Avoir OpenSSL d’installé sur le serveur
Etape 1 – Génération des certificats SSL
Connectez vous en SSH sur votre serveur. Si OpenSSL n’est pas installé sur celui-ci, installez le avec cette commande (si vous êtes sous Debian) :
apt-get install openssl |
Si vous n’avez pas d’accès à SSH vous pouvez toujours installer OpenSSl sur votre poste sous Windows avec les binaires disponibles ici.
NB: Dans la suite de l’article je n’expliquerais pas les commandes sous Windows mais normalement il suffira de vous placer dans le répertoire où l’exécutable OpenSSL.exe est situé et de remplacer les commandes « openssl » par « openssl.exe ».
Pour commencer mettez vous dans un répertoire disponible par FTP (Il faudra par la suite télécharger un certificat pour l’envoyer à Paypal). Ensuite tapez cette commande pour générer la clé privée :
openssl genrsa -out clepriv.pem 1024 |
Puis cette commande pour générer le certificat publique :
openssl req -new -key clepriv.pem -x509 -days 365 -out certpub.pem |
Remplissez les informations demandées (Pays, Email etc…).
Note importante : Le certificat sera ici valable 1 an, il faudra donc penser que dans un an vous devrez re-générer ce certificat ! Vous pouvez augmenter la durée de vie de celui-ci mais ce n’est pas particulièrement recommandé.
Etape 2 – Envoyer le certificat publique à Paypal et télécharger le certificat publique de Paypal
Maintenant il faut envoyer le certificat publique de votre site à Paypal (certpub.pem) et télécharger le certificat publique de Paypal. Pour se faire connectez vous sur votre compte Paypal et allez dans :
- Mon Compte => Préférences => Plus d’Options => Certificats de cryptage pour site marchand (colonne de droite, en bas)
Commencez par télécharger le certificat publique de Paypal en cliquant sur le bouton « Télécharger ». Vous aurez un fichier qui se nomme paypal_cert_pem.txt, renommez le en paypal_cert.pem.
En bas de page, dans la partie « Vos certificats publics » cliquez sur le bouton « Ajouter » et sélectionnez le fichier certpub.pem que vous avez précédemment généré. Vous reviendrez ensuite sur la page de gestion des certificats et vous devriez voir apparaitre une ligne dans le tableau du bas avec comme informations l’ID du certificat, l’autorité de certification (correspondant aux informations saisies lors de la génération de votre certificat publique) et la date d’expiration de celui-ci. Noter dans un coin l’ID du certificat indiqué il servira par la suite.
A cette étape vous aurez donc en votre possession trois fichiers :
- certpub.pem
- clepriv.pem
- paypal_cert.pem
Etape 3 – Mise en place de la génération du bouton en PHP
Tout d’abord placez les trois fichiers pem dans un répertoire de votre serveur inaccessible par le web, pour être plus explicite il doit être en dehors de la racine HTTP de votre serveur mais il doit toujours être accessible par PHP. Si l’option open_base_dir est active sur votre configuration PHP ajoutez ce répertoire pour que PHP puisse y accéder. Si vous ne pouvez pas modifier cette configuration, vous pouvez éventuellement placer les fichiers dans un répertoire de votre site mais vous devrez mettre un .htaccess empêchant l’accès à ces fichiers. Votre .htaccess ressemblera à ceci :
Options -Indexes <Files ~ "\.(pem)$"> order deny,allow deny from all </Files> |
Options -Indexes
A noter que pour un serveur Apache supérieur à la version 1.3 il vaut mieux utiliser :
Options -Indexes <FilesMatch "\.(pem)$"> order deny,allow deny from all </FilesMatch> |
Maintenant il faut générer le bouton en php, pour faire ceci je vais vous fournir une petite classe PHP5 vous permettant de vous simplifier la vie. Créez un fichier qui se nomme PaypalCrypt.class.php et mettez ceci dedans :
<?php class PaypalCrypt{ private $privateKey = ''; private $publicKey = ''; private $paypalKey = ''; private $pathOpenSSL = '/usr/bin/openssl'; private $data = array( 'bn' => 'Boutique_BuyNow_WPS_FR', 'cmd' => '_xclick', 'lc' => 'FR', 'custom' => '', 'invoice' => '', 'currency_code' => 'EUR', 'charset' => 'UTF-8', //Définit le charset utilisé sur le site 'no_shipping' => '1' ); public function __construct(){ // Nothing } public function addData($key, $data){ $this->data[$key] = $data; return $this; } public function setPrivateKey($privateKey){ $this->privateKey = $privateKey; return $this; } public function setPublicKey($publicKey){ $this->publicKey = $publicKey; return $this; } public function setPaypalKey($paypalKey){ $this->paypalKey = $paypalKey; return $this; } public function getCryptedData(){ if (!file_exists($this->privateKey)) throw new Exception('ERROR: MY_KEY_FILE '.$this->privateKey.' not found'); if (!file_exists($this->publicKey)) throw new Exception('ERROR: MY_CERT_FILE '.$this->publicKey.' not found'); if (!file_exists($this->paypalKey)) throw new Exception('ERROR: PAYPAL_CERT_FILE '.$this->paypalKey.' not found'); $openssl_cmd = "$this->pathOpenSSL smime -sign -signer $this->publicKey -inkey $this->privateKey ". "-outform der -nodetach -binary| $this->pathOpenSSL smime -encrypt ". "-des3 -binary -outform pem $this->paypalKey"; $descriptors = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), ); $process = proc_open($openssl_cmd, $descriptors, $pipes); if (is_resource($process)) { foreach ($this->data as $key => &$value) if ($value != "") fwrite($pipes[0], "$key=$value\n"); fflush($pipes[0]); fclose($pipes[0]); $output = ""; while (!feof($pipes[1])) $output .= fgets($pipes[1]); fclose($pipes[1]); $return_value = proc_close($process); return $output; } throw new Exception('ERROR: encryption failed'); } public function setOpenSSLPath($path){ if(!file_exists($path)) throw new Exception('OpenSSLPath "'.$path.'" don\'t exists'); $this->pathOpenSSL = $path; } } ?> |
Cette classe est au final assez simple, elle permet de définir les certificats à utiliser, d’insérer des valeurs avec la méthode addData (Suivant la documentation Paypal pour les paramètres possibles) et de générer la chaîne cryptée via la méthode getCryptedData. Quelques informations sont prédéfinies dans la propriété $data de l’objet, se sont celles qui ne varient pas. Par défaut le chemin vers l’exécutable OpenSSL sur le serveur est /usr/bin/openssl (valable sous Debian), cette valeur peut être modifiée dans le constructeur de l’objet.
Voici donc un exemple d’utilisation pour la vente d’un objet « Boite à meuh » au prix de 10€ sur le compte Paypal « toto@toto.com » :
<?php require('/chemin/vers/PaypalCrypt.class.php'); // Initialisation cryptage Paypal $paypalCrypt = new PaypalCrypt(); $paypalCrypt->setPrivateKey('/chemin/vers/clepriv.pem'); $paypalCrypt->setPublicKey('/chemin/vers/certpub.pem'); $paypalCrypt->setPaypalKey('/chemin/vers/cert/paypal_cert.pem'); $paypalCrypt->addData('cert_id','id_certificat_fourni_par_paypal') ->addData('business','toto@toto.com') ->addData('no_note','1') ->addData('shipping','0') ->addData('tax','0') ->addData('rm','2') ->addData('cbt','Retour sur la boutique') ->addData('custom','id_membre') ->addData('return','http://mon_site.com/return.php') ->addData('cancel_return','http://mon_site.com/cancel.php') ->addData('notify_url','http://mon_site.com/ipn.php') ->addData('amount','10') ->addData('item_name', 'Boite à meuh') ->addData('item_number', 'identifiant_produit'); $data = $paypalCrypt->getCryptedData(); ?> <form action="https://www.paypal.com/fr/cgi-bin/webscr" method="post"> <input type="hidden" name="cmd" value="_s-xclick"> <input type="hidden" name="encrypted" value="<?php echo $data?>"/> <input type="submit" value="Commander" class="input_button"> </form> |
Pour l’information cert_id il faut bien entendu fournir la valeur que Paypal vous a donné à l’étape 2 (Oui oui j’ai écrit qu’il fallait le noter
).
Et voilà vous avez un joli bouton Paypal crypté en SSL pour faire vos demandes de paiement. Pour connaître toutes les valeurs possibles que vous pouvez ajouter je vous laisse regarder la documentation Paypal. N’oubliez pas non plus que cela ne vous dispense pas de faire toutes les vérifications nécessaires lors du feedback de Paypal (IPN) pour vérifier la cohérence entre la commande passée chez vous et le paiement que vous avez reçu.
MAJ 30/08/2011: Changement de méthode pour l’appel à OpenSSL pour maximiser la compatibilité avec les différentes plateformes (Windows).
| Imprimer l'article | Cette entrée a été posté par Steuf le 30 août 2011 à 12 h 10 min, et placée dans PHP5/POO, Sécurité. Vous pouvez suivre les réponses à cette entrée via RSS 2.0. Vous pouvez laisser une réponse, ou bien un trackback depuis votre site. |


about 3 years ago
Bonjour et merci pour ce petit article, existe t’il un moyen de securisé le bouton paypal sans passer par le mode ssl.
J’ai fais une appli et je ne connaissais pas cette méthode avec firebug, je viens de tester et effectivement ca marche
Ca me fait peur tout ca …
about 3 years ago
Bonjour,
Le deuxième moyen est indiqué au début, utiliser l’interface Paypal et créer le bouton dessus, il vous donnera une version sécurisé en SSL, mais comme dit aucune flexibilité sur votre site.
Après vous n’avez pas de bille à vous faire si au niveau du Feedback vous vérifiez que le paiement reçu correspond bien au paiement demandé… Si ce n’est pas fait, effectivement y’a de quoi avoir peur
about 2 years ago
Bonjour,
Un bel article !
.
C’est marrant ça, j’ai traité exactement la même problématique tout récemment pour un site client
about 2 years ago
Salut Pierrick,
Merci, ça fait plaisir de te voir passer par ici
Hésite pas à faire un coucou à l’occasion. J’espère que le boulo ça va
about 2 years ago
Bonjour,
J’ai un très gros souci avec le script pour une adaptation en mode panier.
J’ajoute cette ligne au script :
Je change celle-ci
puis transforme :
->addData(‘item_name_1′,’test2′)
->addData(‘amount_1′,’5′)
Malheureusement Paypal m’affiche une erreur comme quoi mon panier est vide. Je ne sais vraiment plus quoi faire.
Un coup de pouce me ferais extrêmement plaisir.
Merci par avance pour votre réponse.
about 2 years ago
Il manque la quantité je pense :
->addData(‘quantity_1′,’1′)
about 2 years ago
Malheureusement elle est présente également.
about 2 years ago
et non obligatoire
about 2 years ago
Je désespère …
about 2 years ago
Il faudrait que je vois ton code en entier… Difficile à dire comme ça, tu as sans doute oublié un paramètre cette classe ne fait pas de miracles
Elle ne fait que crypter ce que tu lui donne.
Contacte moi à administrateurATexinsidephp.com si tu veux que j’essaye de voir avec toi ce qui cloche dans ton script.
about 1 year ago
Pour l’utilisation de plusieurs item dans Paypal avec item_name_x, item_number_x, amount_x, quantity_x, tu doit absolument absolument utiliser l’option upload = 1 addData(‘upload ‘,’1′) . Tu doit aussi avoir la description total de ton pannier avec item_name. Tu peux aussi rajouter la livraison avec l’option handling_cart, et les taxes avec l’option tax_cart. A aussi met _cart pour la valeur de cmd – addData(‘cmd’,'_cart’);
about 1 year ago
Bonjour,
je vous remercie pour cette classe qui m’a permis d’avancer… malheureusement qu’un peu.
Lorsque la commande openSSL est excutée, elle ne produit qu’un enregistrement très court (8 lignes) comparativement à d’autres que je vois sur le web. Le bouton ainsi constitué génère une erreur : »manque adresse proprio dans le blob »
Ce que j’ai vérifié :
Les fichiers pem sont tous présents et sont à priori corrects
Les données fournies à $data sont correctes, $data est ok à la sortie (une longue chaîne comportant tous les éléments)
Le bouton fonctionne en non crypté : les valeurs de $data sont donc à priori correctes et cohérentes
ID certificat paypal ok
ID marchand OK
certificat Paypal OK
clef privée : telle que fournie par l’hébergeur
certificat tel que fourni par l’hébergeur
J’ai immanquablement manqué quelque chose….
Pourriez-vous me suggérer des pistes, s’il vous plaît ?
Merci à vous
about 1 year ago
Bonjour, quel est votre hébergeur ?
Je viens de mettre à jour la class, vous pouvez l’essayer de nouveau ? En effet la précédente avait quelques soucis de compatibilité avec certaines plateformes avec un peu de chance cela réglera votre problème.
about 1 year ago
salut et merci pour le tuto, malhereusement j’ai un problème un message de paypal: nous ne parvenons pas à décrypter l’id du certificat y a t’il une solution?
about 11 months ago
Bonjour, j’ai un petit problème avec ce script, très bien fait par ailleurs, félicitations.
il me renvoie bien une chaine encodée et très longue dans le input de nom « encrypted » mais cela ne fonctionne pas. Paypal dit qu’il y a un problème avec le site.
J’appelle le script en ajax pour générer à la volée les boutons selon les choix des utilisateurs.
ID certificat paypal ok
ID marchand OK
certificat Paypal OK
clef privée : je l’ai générée sur mon dédié kimsufi
clef publique : idem
un brin d’aide serait super bienvenu.
about 11 months ago
Bonjour quelle erreur Paypal vous renvoie il exactement ?