Bonjour à tous,
J'utilise le bundle PayumBundle qui permet aux utilisateurs d'acheter en ligne avec paypal. Pour le moment je travaille uniquement avec la sandbox.
Sur mon site de développement en local, tout fonctionne bien. on est bien redirigé sur le site de paypal quand on clique sur le bouton "acheter maintenant", on arrive à faire le paiement jusqu'au bout. Sur mon site en ligne par contre, j'obtiens systématiquement une erreur 81002, et malgré mes recherche sur le web, je n'ai pas vraiment compris d'où pouvait venir le problème et comment le résoudre.
La version locale et celle en ligne étant synchronisées par git (i.e. la version en ligne est automatiquement pull quand je push depuis ma version locale), le code du contrôleur utilisant Payum et le config.yml sont identiques. Par contre vu que le dossier vendor n'est pas synchronisé par défaut, ça me paraissait moins problématique de ne pas partager les trucs de composer et en particuler le composer.json. A priori ce sont également les mêmes, en tout cas en ce qui concerne la partie dépendances. La seule différence significative étant donc les environnements me semble-t-il.
Voici ce que je reçois comme erreur au moment où je clique sur mon bouton "acheter maintenant" :
1 | {"status":"failed","payment":{"total_amount":1490,"currency_code":"CHF","details":{"INVNUM":"5722f6d6b8476","PAYMENTREQUEST_0_CURRENCYCODE":"CHF","PAYMENTREQUEST_0_AMT":14.9,"PAYMENTREQUEST_0_DESC":"payment_dperiod0","PAYMENTREQUEST_0_PAYMENTACTION":"Sale","AUTHORIZE_TOKEN_USERACTION":"commit","RETURNURL":"http:\/\/monsite.com\/payment\/capture\/ohGauNaZrfMyNaAriyLefKhNFIfXKn8WRQtwKTICtUU","CANCELURL":"http:\/\/monsite.com\/payment\/capture\/ohGauNaZrfMyNaAriyLefKhNFIfXKn8WRQtwKTICtUU?cancelled=1","PAYMENTREQUEST_0_NOTIFYURL":"http:\/\/monsite.com\/payment\/notify\/LP_1DwyfchzB5X1Z8_oCE5NLWwGFJ5uAZhVEJfCjS0c","ACK":"Failure","L_ERRORCODE0":"81002","L_SHORTMESSAGE0":"UnspecifiedMethod","L_LONGMESSAGE0":"Method Specified is not Supported","L_SEVERITYCODE0":"Error"}}} |
Voici les parties qui me paraissent intéressantes du contrôleur. Je suis très largement parti du code d'exemple donné dans la doc du bundle, pour un paiement simple utilisant paypal express checkout / paypal NVP.
Je ne sais pas si c'est réellement le moyen le plus simple et le meilleur pour permettre aux utilisateur de payer mon service en ligne sur mon site, car j'ai l'impression que l'acheteur doit obligatoirement avoir un compte paypal (ce qui me dérange un peu car j'ai peur que ce soit un peu bloquant) au cas où il n'en a pas, et je n'ai pas besoin de l'adresse postale (car ce que je vends est purement immatériel). Si vous pensez que je devrais utiliser une autre API, je prends volontiers vos conseils.
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 | <?php /** * @Route("/{_locale}/activation/pay", name="paymentNew", requirements={"_locale"="%locales%"}) */ public function newAction(Request $request) { $user = $this->getUser(); if ($this->isGranted('ROLE_USER')) { $form = $this->createForm('AppBundle\Form\PaymentForm'); $form->handleRequest($request); if ($form->isValid()) { //... $gatewayName = 'paypal_express_checkout'; $storage = $this->get('payum')->getStorage('AppBundle\Entity\Payment'); $payment = $storage->create(); $payment->setCurrencyCode('CHF'); $payment->setTotalAmount(floor(100*$cost)); $payment->setClientEmail($user->getEmail()); $payment->setClientId($user->getId()); $payment->setNumber(uniqid()); $payment->setDescription($this->trans("payment_d$key", array(), 'activation')); $storage->update($payment); $captureToken = $this->get('payum')->getTokenFactory()->createCaptureToken( $gatewayName, $payment, 'paymentDone' ); return $this->redirect($captureToken->getTargetUrl()); }} } return $this->render('paymentNew.twig', [ 'base_dir' => realpath($this->container->getParameter('kernel.root_dir').'/..'), //... 'form' => @$form?$form->createView() :null, ]); } /** * @Route("/{_locale}/payment/done", name="paymentDone", requirements={"_locale"="%locales%"}) */ public function doneAction(Request $request) { $this->denyAccessUnlessGranted('ROLE_USER'); $token = $this->get('payum')->getHttpRequestVerifier()->verify($request); $gateway = $this->get('payum')->getGateway($token->getGatewayName()); $this->get('payum')->getHttpRequestVerifier()->invalidate($token); $gateway->execute($status = new GetHumanStatus($token)); $payment = $status->getFirstModel(); $status = strtolower($status->getValue()); $amount = $payment->getTotalAmount()/100.0; if ($status=='captured') { //... le paiement a réussi, on met à jour le compte utilisateur ... return $this->render('paymentSuccess.twig', [ 'base_dir' => realpath($this->container->getParameter('kernel.root_dir').'/..'), ]); } else { // Le paiement a foiré, on affiche une erreur //$this->addFlash('error', $this->trans('PaymentFailed', array(), 'activation')); //return $this->redirectToRoute('paymentPrepare', array('_locale'=>$request->getLocale())); echo \nl2br(\json_encode(array( 'status' => $status, 'payment' => array( 'total_amount' => $payment->getTotalAmount(), 'currency_code' => $payment->getCurrencyCode(), 'details' => $payment->getDetails(), ), ))); exit(); }} |
Voici la partie relative à Payum dans le config.yml :
1 2 3 4 5 6 7 8 9 10 11 12 13 | payum: security: token_storage: AppBundle\Entity\PaymentToken: { doctrine: orm } storages: AppBundle\Entity\Payment: { doctrine: orm } gateways: paypal_express_checkout: factory: paypal_express_checkout username: %payment_username% password: %payment_password% signature: %payment_signature% sandbox: %payment_sandbox% |
ET le parameters.yml :
1 2 3 4 | payment_username: XXX-XXX_api1.XXX.com # Le nom de mon compte payment_password: XXX # Le mot de passe fourni par paypal sandbox payment_signature: XXX # La clé fournie par paypal sandbox payment_sandbox: true |
ET le composer.json :
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 | { "name": "My application", "license": "proprietary", "type": "project", "autoload": { "psr-4": { "": "src/" }, "classmap": [ "app/AppKernel.php", "app/AppCache.php" ] }, "autoload-dev": { "psr-4": { "Tests\\": "tests/" } }, "require": { "php": ">=5.5.9", "symfony/symfony": "3.0.*", "doctrine/orm": "^2.5", "doctrine/doctrine-bundle": "^1.6", "doctrine/doctrine-cache-bundle": "^1.2", "symfony/swiftmailer-bundle": "^2.3", "symfony/monolog-bundle": "^2.8", "sensio/distribution-bundle": "^5.0", "sensio/framework-extra-bundle": "^3.0.2", "incenteev/composer-parameter-handler": "^2.0", "friendsofsymfony/user-bundle": "~2.0@dev", "doctrine/doctrine-fixtures-bundle": "^2.3", "willdurand/js-translation-bundle": "^2.5", "payum/payum-bundle": "^2.0", "payum/offline": "^1.3", "payum/paypal-express-checkout-nvp": "^1.3", "captcha-com/symfony-captcha-bundle": "4.*", "php-http/guzzle6-adapter": "^1.0" }, "require-dev": { "sensio/generator-bundle": "^3.0", "symfony/phpunit-bridge": "^2.7" }, "scripts": { "post-install-cmd": [ "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::prepareDeploymentTarget" ], "post-update-cmd": [ "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::buildBootstrap", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::clearCache", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installAssets", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::installRequirementsFile", "Sensio\\Bundle\\DistributionBundle\\Composer\\ScriptHandler::prepareDeploymentTarget" ] }, "extra": { "symfony-app-dir": "app", "symfony-bin-dir": "bin", "symfony-var-dir": "var", "symfony-web-dir": "web", "symfony-tests-dir": "tests", "symfony-assets-install": "relative", "incenteev-parameters": { "file": "app/config/parameters.yml" } } } |
Comme je l'ai dit, la seule différence évidente d'après moi entre la version en local qui fonctionne et la version en ligne qui ne fonctionne pas, ce sont les environnements. A savoir :
En local: Windows 7 64 bits avec WAMP incluant PHP 5.5.12 et MySQL 5.6.17
En ligne: debian 8.0 64 bits à jour avec PHP 5.6.19 et MariaDB 10.0.23
Composer et symfony me paraissent à jour, j'ai exécuté plusieurs fois php composer.phar self-update
et php composer.phar update
de part et d'autre.
Évidemment un vidage du cache même en virant carrément le dossier var/cache à la main ne change rien au problème.
Quelqu'un a-t-il une idée ?
Merci pour vos réponses.