From b2ea74e4e6ca98a08ccadd7f37926e7ec594ea2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 7 Nov 2013 19:04:44 +0100 Subject: [REF] payment: moved paypal and ogone stuff into their dedicated modules. Added payment_acquirer_paypal and .._ogone modules. Those modules hold the model / data / controllers related to the specific acquirer. Also updated website_payment, to use website.menu instead of custom change in header, as menus are now editable. bzr revid: tde@openerp.com-20131107180444-bbzobquqq9o6pctv --- addons/payment_acquirer/__openerp__.py | 4 - addons/payment_acquirer/controllers/main.py | 70 +-- addons/payment_acquirer/data/__init__.py | 22 - addons/payment_acquirer/data/ogone.py | 497 --------------------- addons/payment_acquirer/data/ogone.xml | 17 - addons/payment_acquirer/data/paypal.xml | 15 - addons/payment_acquirer/models/__init__.py | 2 - addons/payment_acquirer/models/ogone.py | 397 ---------------- addons/payment_acquirer/models/paypal.py | 241 ---------- addons/payment_acquirer/tests/__init__.py | 8 +- addons/payment_acquirer/tests/common.py | 4 +- .../tests/test_payment_acquirer.py | 272 ----------- addons/payment_acquirer/views/ogone.xml | 44 -- addons/payment_acquirer/views/paypal.xml | 34 -- addons/payment_acquirer_ogone/__init__.py | 23 + addons/payment_acquirer_ogone/__openerp__.py | 35 ++ .../payment_acquirer_ogone/controllers/__init__.py | 1 + addons/payment_acquirer_ogone/controllers/main.py | 53 +++ addons/payment_acquirer_ogone/data/__init__.py | 22 + addons/payment_acquirer_ogone/data/ogone.py | 497 +++++++++++++++++++++ addons/payment_acquirer_ogone/data/ogone.xml | 17 + addons/payment_acquirer_ogone/models/__init__.py | 22 + addons/payment_acquirer_ogone/models/ogone.py | 397 ++++++++++++++++ addons/payment_acquirer_ogone/tests/__init__.py | 28 ++ addons/payment_acquirer_ogone/tests/test_ogone.py | 190 ++++++++ addons/payment_acquirer_ogone/views/ogone.xml | 44 ++ addons/payment_acquirer_paypal/__init__.py | 23 + addons/payment_acquirer_paypal/__openerp__.py | 35 ++ .../controllers/__init__.py | 1 + addons/payment_acquirer_paypal/controllers/main.py | 74 +++ addons/payment_acquirer_paypal/data/paypal.xml | 15 + addons/payment_acquirer_paypal/models/__init__.py | 22 + addons/payment_acquirer_paypal/models/paypal.py | 241 ++++++++++ addons/payment_acquirer_paypal/tests/__init__.py | 28 ++ .../payment_acquirer_paypal/tests/test_paypal.py | 112 +++++ addons/payment_acquirer_paypal/views/paypal.xml | 34 ++ addons/website_payment/__openerp__.py | 2 +- .../views/website_payment_templates.xml | 69 +-- 38 files changed, 1959 insertions(+), 1653 deletions(-) delete mode 100644 addons/payment_acquirer/data/__init__.py delete mode 100644 addons/payment_acquirer/data/ogone.py delete mode 100644 addons/payment_acquirer/data/ogone.xml delete mode 100644 addons/payment_acquirer/data/paypal.xml delete mode 100644 addons/payment_acquirer/models/ogone.py delete mode 100644 addons/payment_acquirer/models/paypal.py delete mode 100644 addons/payment_acquirer/tests/test_payment_acquirer.py delete mode 100644 addons/payment_acquirer/views/ogone.xml delete mode 100644 addons/payment_acquirer/views/paypal.xml create mode 100644 addons/payment_acquirer_ogone/__init__.py create mode 100644 addons/payment_acquirer_ogone/__openerp__.py create mode 100644 addons/payment_acquirer_ogone/controllers/__init__.py create mode 100644 addons/payment_acquirer_ogone/controllers/main.py create mode 100644 addons/payment_acquirer_ogone/data/__init__.py create mode 100644 addons/payment_acquirer_ogone/data/ogone.py create mode 100644 addons/payment_acquirer_ogone/data/ogone.xml create mode 100644 addons/payment_acquirer_ogone/models/__init__.py create mode 100644 addons/payment_acquirer_ogone/models/ogone.py create mode 100644 addons/payment_acquirer_ogone/tests/__init__.py create mode 100644 addons/payment_acquirer_ogone/tests/test_ogone.py create mode 100644 addons/payment_acquirer_ogone/views/ogone.xml create mode 100644 addons/payment_acquirer_paypal/__init__.py create mode 100644 addons/payment_acquirer_paypal/__openerp__.py create mode 100644 addons/payment_acquirer_paypal/controllers/__init__.py create mode 100644 addons/payment_acquirer_paypal/controllers/main.py create mode 100644 addons/payment_acquirer_paypal/data/paypal.xml create mode 100644 addons/payment_acquirer_paypal/models/__init__.py create mode 100644 addons/payment_acquirer_paypal/models/paypal.py create mode 100644 addons/payment_acquirer_paypal/tests/__init__.py create mode 100644 addons/payment_acquirer_paypal/tests/test_paypal.py create mode 100644 addons/payment_acquirer_paypal/views/paypal.xml diff --git a/addons/payment_acquirer/__openerp__.py b/addons/payment_acquirer/__openerp__.py index 6fd1d82489a..164e1d77318 100644 --- a/addons/payment_acquirer/__openerp__.py +++ b/addons/payment_acquirer/__openerp__.py @@ -29,11 +29,7 @@ 'depends': ['decimal_precision', 'mail'], 'data': [ 'views/payment_acquirer_views.xml', - 'views/ogone.xml', - 'views/paypal.xml', 'data/payment_acquirer_data.xml', - 'data/ogone.xml', - 'data/paypal.xml', 'security/ir.model.access.csv', ], 'installable': True, diff --git a/addons/payment_acquirer/controllers/main.py b/addons/payment_acquirer/controllers/main.py index 9f1dcdc0f58..c0ef7f383d4 100644 --- a/addons/payment_acquirer/controllers/main.py +++ b/addons/payment_acquirer/controllers/main.py @@ -20,77 +20,11 @@ ############################################################################## from openerp.addons.web import http -from openerp.addons.web.http import request -# from openerp.addons.payment_acquirer.models.payment_acquirer import ValidationError -from openerp.addons.website.models import website import logging -import requests -from urllib import urlencode _logger = logging.getLogger(__name__) -class PaypalController(http.Controller): - _notify_url = '/payment/paypal/ipn/' - _return_url = '/payment/paypal/dpn/' - _cancel_url = '/payment/paypal/cancel/' - # _ipn_url2 = '/payment/paypal//ipn/' - - @website.route('/payment/paypal//ipn/', type='http', auth='admin') - def paypal_ipn(self, **post): - print 'Entering paypal_ipn with post', post - # step 1: return an empty HTTP 200 response -> will be done at the end by returning '' - - # step 2: POST the complete, unaltered message back to Paypal (preceded by cmd=_notify-validate), with same encoding - paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr" - post_url = '%s?cmd=_notify-validate&%s' % (paypal_url, urlencode(post)) - resp = requests.post(post_url) - print '\tReceived response', resp, resp.text - - # step 3: paypal send either VERIFIED or INVALID (single word) - if resp.text == 'VERIFIED': - # _logger.warning('') - cr, uid, context = request.cr, request.uid, request.context - # payment_transaction = request.registry['payment.transaction'] - # payment_transaction.validate() - elif resp.text == 'INVALID': - # _logger.warning('') - pass - else: - # _logger.warning('') -> something went wrong - pass - - return '' - - @website.route([ - '/payment/paypal/test/dpn', - ], type='http', auth="public") - def paypal_test_success(self, **post): - """ TODO - """ - cr, uid, context = request.cr, request.uid, request.context - print post - return '' - - -class OgoneController(http.Controller): - _accept_url = '/payment/ogone/test/accept' - _decline_url = '/payment/ogone/test/decline' - _exception_url = '/payment/ogone/test/exception' - _cancel_url = '/payment/ogone/test/cancel' - - @website.route([ - '/payment/ogone/feedback', '/payment/ogone/test/accept', - '/payment/ogone/decline', '/payment/ogone/test/decline', - '/payment/ogone/exception', '/payment/ogone/test/exception', - '/payment/ogone/cancel', '/payment/ogone/test/cancel', - ], type='http', auth='admin') - def feedback(self, **post): - cr, uid, context = request.cr, request.uid, request.context - Payment = request.registry['payment.transaction'] - print 'Entering ogone feedback with', post - - res = Payment.tx_ogone_feedback(cr, uid, post, context) - print res - return '' +class PaymentController(http.Controller): + pass diff --git a/addons/payment_acquirer/data/__init__.py b/addons/payment_acquirer/data/__init__.py deleted file mode 100644 index 61f4cbeacf3..00000000000 --- a/addons/payment_acquirer/data/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2013-Today OpenERP SA (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -import ogone diff --git a/addons/payment_acquirer/data/ogone.py b/addons/payment_acquirer/data/ogone.py deleted file mode 100644 index 3a2500ee24b..00000000000 --- a/addons/payment_acquirer/data/ogone.py +++ /dev/null @@ -1,497 +0,0 @@ -# -*- coding: utf-8 -*- - -OGONE_ERROR_MAP = { - '0020001001': "Authorization failed, please retry", - '0020001002': "Authorization failed, please retry", - '0020001003': "Authorization failed, please retry", - '0020001004': "Authorization failed, please retry", - '0020001005': "Authorization failed, please retry", - '0020001006': "Authorization failed, please retry", - '0020001007': "Authorization failed, please retry", - '0020001008': "Authorization failed, please retry", - '0020001009': "Authorization failed, please retry", - '0020001010': "Authorization failed, please retry", - '0030001999': "Our payment system is currently under maintenance, please try later", - '0050001005': "Expiry date error", - '0050001007': "Requested Operation code not allowed", - '0050001008': "Invalid delay value", - '0050001010': "Input date in invalid format", - '0050001013': "Unable to parse socket input stream", - '0050001014': "Error in parsing stream content", - '0050001015': "Currency error", - '0050001016': "Transaction still posted at end of wait", - '0050001017': "Sync value not compatible with delay value", - '0050001019': "Transaction duplicate of a pre-existing transaction", - '0050001020': "Acceptation code empty while required for the transaction", - '0050001024': "Maintenance acquirer differs from original transaction acquirer", - '0050001025': "Maintenance merchant differs from original transaction merchant", - '0050001028': "Maintenance operation not accurate for the original transaction", - '0050001031': "Host application unknown for the transaction", - '0050001032': "Unable to perform requested operation with requested currency", - '0050001033': "Maintenance card number differs from original transaction card number", - '0050001034': "Operation code not allowed", - '0050001035': "Exception occurred in socket input stream treatment", - '0050001036': "Card length does not correspond to an acceptable value for the brand", - '0050001036': "Card length does not correspond to an acceptable value for the brand", - '0050001068': "A technical problem occurred, please contact helpdesk", - '0050001069': "Invalid check for CardID and Brand", - '0050001070': "A technical problem occurred, please contact helpdesk", - '0050001116': "Unknown origin IP", - '0050001117': "No origin IP detected", - '0050001118': "Merchant configuration problem, please contact support", - '10001001': "Communication failure", - '10001002': "Communication failure", - '10001003': "Communication failure", - '10001004': "Communication failure", - '10001005': "Communication failure", - '20001001': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", - '20001002': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", - '20001003': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", - '20001004': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", - '20001005': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", - '20001006': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", - '20001007': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", - '20001008': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", - '20001009': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", - '20001010': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", - '20001101': "A technical problem occurred, please contact helpdesk", - '20001105': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", - '20001111': "A technical problem occurred, please contact helpdesk", - '20002001': "Origin for the response of the bank can not be checked", - '20002002': "Beneficiary account number has been modified during processing", - '20002003': "Amount has been modified during processing", - '20002004': "Currency has been modified during processing", - '20002005': "No feedback from the bank server has been detected", - '30001001': "Payment refused by the acquirer", - '30001002': "Duplicate request", - '30001010': "A technical problem occurred, please contact helpdesk", - '30001011': "A technical problem occurred, please contact helpdesk", - '30001012': "Card black listed - Contact acquirer", - '30001015': "Your merchant's acquirer is temporarily unavailable, please try later or choose another payment method.", - '30001051': "A technical problem occurred, please contact helpdesk", - '30001054': "A technical problem occurred, please contact helpdesk", - '30001057': "Your merchant's acquirer is temporarily unavailable, please try later or choose another payment method.", - '30001058': "Your merchant's acquirer is temporarily unavailable, please try later or choose another payment method.", - '30001060': "Aquirer indicates that a failure occured during payment processing", - '30001070': "RATEPAY Invalid Response Type (Failure)", - '30001071': "RATEPAY Missing Mandatory status code field (failure)", - '30001072': "RATEPAY Missing Mandatory Result code field (failure)", - '30001073': "RATEPAY Response parsing Failed", - '30001090': "CVC check required by front end and returned invalid by acquirer", - '30001091': "ZIP check required by front end and returned invalid by acquirer", - '30001092': "Address check required by front end and returned as invalid by acquirer.", - '30001100': "Unauthorized buyer's country", - '30001101': "IP country <> card country", - '30001102': "Number of different countries too high", - '30001103': "unauthorized card country", - '30001104': "unauthorized ip address country", - '30001105': "Anonymous proxy", - '30001110': "If the problem persists, please contact Support, or go to paysafecard's card balance page (https://customer.cc.at.paysafecard.com/psccustomer/GetWelcomePanelServlet?language=en) to see when the amount reserved on your card will be available again.", - '30001120': "IP address in merchant's black list", - '30001130': "BIN in merchant's black list", - '30001131': "Wrong BIN for 3xCB", - '30001140': "Card in merchant's card blacklist", - '30001141': "Email in blacklist", - '30001142': "Passenger name in blacklist", - '30001143': "Card holder name in blacklist", - '30001144': "Passenger name different from owner name", - '30001145': "Time to departure too short", - '30001149': "Card Configured in Card Supplier Limit for another relation (CSL)", - '30001150': "Card not configured in the system for this customer (CSL)", - '30001151': "REF1 not allowed for this relationship (Contract number", - '30001152': "Card/Supplier Amount limit reached (CSL)", - '30001153': "Card not allowed for this supplier (Date out of contract bounds)", - '30001154': "You have reached the usage limit allowed", - '30001155': "You have reached the usage limit allowed", - '30001156': "You have reached the usage limit allowed", - '30001157': "Unauthorized IP country for itinerary", - '30001158': "email usage limit reached", - '30001159': "Unauthorized card country/IP country combination", - '30001160': "Postcode in highrisk group", - '30001161': "generic blacklist match", - '30001162': "Billing Address is a PO Box", - '30001180': "maximum scoring reached", - '30001997': "Authorization canceled by simulation", - '30001998': "A technical problem occurred, please try again.", - '30001999': "Your merchant's acquirer is temporarily unavailable, please try later or choose another payment method.", - '30002001': "Payment refused by the financial institution", - '30002001': "Payment refused by the financial institution", - '30021001': "Call acquirer support call number.", - '30022001': "Payment must be approved by the acquirer before execution.", - '30031001': "Invalid merchant number.", - '30041001': "Retain card.", - '30051001': "Authorization declined", - '30071001': "Retain card - special conditions.", - '30121001': "Invalid transaction", - '30131001': "Invalid amount", - '30131002': "You have reached the total amount allowed", - '30141001': "Invalid card number", - '30151001': "Unknown acquiring institution.", - '30171001': "Payment method cancelled by the buyer", - '30171002': "The maximum time allowed is elapsed.", - '30191001': "Try again later.", - '30201001': "A technical problem occurred, please contact helpdesk", - '30301001': "Invalid format", - '30311001': "Unknown acquirer ID.", - '30331001': "Card expired.", - '30341001': "Suspicion of fraud.", - '30341002': "Suspicion of fraud (3rdMan)", - '30341003': "Suspicion of fraud (Perseuss)", - '30341004': "Suspicion of fraud (ETHOCA)", - '30381001': "A technical problem occurred, please contact helpdesk", - '30401001': "Invalid function.", - '30411001': "Lost card.", - '30431001': "Stolen card, pick up", - '30511001': "Insufficient funds.", - '30521001': "No Authorization. Contact the issuer of your card.", - '30541001': "Card expired.", - '30551001': "Invalid PIN.", - '30561001': "Card not in authorizer's database.", - '30571001': "Transaction not permitted on card.", - '30581001': "Transaction not allowed on this terminal", - '30591001': "Suspicion of fraud.", - '30601001': "The merchant must contact the acquirer.", - '30611001': "Amount exceeds card ceiling.", - '30621001': "Restricted card.", - '30631001': "Security policy not respected.", - '30641001': "Amount changed from ref. trn.", - '30681001': "Tardy response.", - '30751001': "PIN entered incorrectly too often", - '30761001': "Card holder already contesting.", - '30771001': "PIN entry required.", - '30811001': "Message flow error.", - '30821001': "Authorization center unavailable", - '30831001': "Authorization center unavailable", - '30901001': "Temporary system shutdown.", - '30911001': "Acquirer unavailable.", - '30921001': "Invalid card type for acquirer.", - '30941001': "Duplicate transaction", - '30961001': "Processing temporarily not possible", - '30971001': "A technical problem occurred, please contact helpdesk", - '30981001': "A technical problem occurred, please contact helpdesk", - '31011001': "Unknown acceptance code", - '31021001': "Invalid currency", - '31031001': "Acceptance code missing", - '31041001': "Inactive card", - '31051001': "Merchant not active", - '31061001': "Invalid expiration date", - '31071001': "Interrupted host communication", - '31081001': "Card refused", - '31091001': "Invalid password", - '31101001': "Plafond transaction (majoré du bonus) dépassé", - '31111001': "Plafond mensuel (majoré du bonus) dépassé", - '31121001': "Plafond centre de facturation dépassé", - '31131001': "Plafond entreprise dépassé", - '31141001': "Code MCC du fournisseur non autorisé pour la carte", - '31151001': "Numéro SIRET du fournisseur non autorisé pour la carte", - '31161001': "This is not a valid online banking account", - '32001004': "A technical problem occurred, please try again.", - '34011001': "Bezahlung mit RatePAY nicht möglich.", - '39991001': "A technical problem occurred, please contact the helpdesk of your acquirer", - '40001001': "A technical problem occurred, please try again.", - '40001002': "A technical problem occurred, please try again.", - '40001003': "A technical problem occurred, please try again.", - '40001004': "A technical problem occurred, please try again.", - '40001005': "A technical problem occurred, please try again.", - '40001006': "A technical problem occurred, please try again.", - '40001007': "A technical problem occurred, please try again.", - '40001008': "A technical problem occurred, please try again.", - '40001009': "A technical problem occurred, please try again.", - '40001010': "A technical problem occurred, please try again.", - '40001011': "A technical problem occurred, please contact helpdesk", - '40001012': "Your merchant's acquirer is temporarily unavailable, please try later or choose another payment method.", - '40001013': "A technical problem occurred, please contact helpdesk", - '40001016': "A technical problem occurred, please contact helpdesk", - '40001018': "A technical problem occurred, please try again.", - '40001019': "Sorry, an error occurred during processing. Please retry the operation (use back button of the browser). If problem persists, contact your merchant's helpdesk.", - '40001020': "Sorry, an error occurred during processing. Please retry the operation (use back button of the browser). If problem persists, contact your merchant's helpdesk.", - '40001050': "A technical problem occurred, please contact helpdesk", - '40001133': "Authentication failed, the signature of your bank access control server is incorrect", - '40001134': "Authentication failed, please retry or cancel.", - '40001135': "Authentication temporary unavailable, please retry or cancel.", - '40001136': "Technical problem with your browser, please retry or cancel", - '40001137': "Your bank access control server is temporary unavailable, please retry or cancel", - '40001998': "Temporary technical problem. Please retry a little bit later.", - '50001001': "Unknown card type", - '50001002': "Card number format check failed for given card number.", - '50001003': "Merchant data error", - '50001004': "Merchant identification missing", - '50001005': "Expiry date error", - '50001006': "Amount is not a number", - '50001007': "A technical problem occurred, please contact helpdesk", - '50001008': "A technical problem occurred, please contact helpdesk", - '50001009': "A technical problem occurred, please contact helpdesk", - '50001010': "A technical problem occurred, please contact helpdesk", - '50001011': "Brand not supported for that merchant", - '50001012': "A technical problem occurred, please contact helpdesk", - '50001013': "A technical problem occurred, please contact helpdesk", - '50001014': "A technical problem occurred, please contact helpdesk", - '50001015': "Invalid currency code", - '50001016': "A technical problem occurred, please contact helpdesk", - '50001017': "A technical problem occurred, please contact helpdesk", - '50001018': "A technical problem occurred, please contact helpdesk", - '50001019': "A technical problem occurred, please contact helpdesk", - '50001020': "A technical problem occurred, please contact helpdesk", - '50001021': "A technical problem occurred, please contact helpdesk", - '50001022': "A technical problem occurred, please contact helpdesk", - '50001023': "A technical problem occurred, please contact helpdesk", - '50001024': "A technical problem occurred, please contact helpdesk", - '50001025': "A technical problem occurred, please contact helpdesk", - '50001026': "A technical problem occurred, please contact helpdesk", - '50001027': "A technical problem occurred, please contact helpdesk", - '50001028': "A technical problem occurred, please contact helpdesk", - '50001029': "A technical problem occurred, please contact helpdesk", - '50001030': "A technical problem occurred, please contact helpdesk", - '50001031': "A technical problem occurred, please contact helpdesk", - '50001032': "A technical problem occurred, please contact helpdesk", - '50001033': "A technical problem occurred, please contact helpdesk", - '50001034': "A technical problem occurred, please contact helpdesk", - '50001035': "A technical problem occurred, please contact helpdesk", - '50001036': "Card length does not correspond to an acceptable value for the brand", - '50001037': "Purchasing card number for a regular merchant", - '50001038': "Non Purchasing card for a Purchasing card merchant", - '50001039': "Details sent for a non-Purchasing card merchant, please contact helpdesk", - '50001040': "Details not sent for a Purchasing card transaction, please contact helpdesk", - '50001041': "Payment detail validation failed", - '50001042': "Given transactions amounts (tax,discount,shipping,net,etc…) do not compute correctly together", - '50001043': "A technical problem occurred, please contact helpdesk", - '50001044': "No acquirer configured for this operation", - '50001045': "No UID configured for this operation", - '50001046': "Operation not allowed for the merchant", - '50001047': "A technical problem occurred, please contact helpdesk", - '50001048': "A technical problem occurred, please contact helpdesk", - '50001049': "A technical problem occurred, please contact helpdesk", - '50001050': "A technical problem occurred, please contact helpdesk", - '50001051': "A technical problem occurred, please contact helpdesk", - '50001052': "A technical problem occurred, please contact helpdesk", - '50001053': "A technical problem occurred, please contact helpdesk", - '50001054': "Card number incorrect or incompatible", - '50001055': "A technical problem occurred, please contact helpdesk", - '50001056': "A technical problem occurred, please contact helpdesk", - '50001057': "A technical problem occurred, please contact helpdesk", - '50001058': "A technical problem occurred, please contact helpdesk", - '50001059': "A technical problem occurred, please contact helpdesk", - '50001060': "A technical problem occurred, please contact helpdesk", - '50001061': "A technical problem occurred, please contact helpdesk", - '50001062': "A technical problem occurred, please contact helpdesk", - '50001063': "Card Issue Number does not correspond to range or not present", - '50001064': "Start Date not valid or not present", - '50001066': "Format of CVC code invalid", - '50001067': "The merchant is not enrolled for 3D-Secure", - '50001068': "The card number or account number (PAN) is invalid", - '50001069': "Invalid check for CardID and Brand", - '50001070': "The ECI value given is either not supported, or in conflict with other data in the transaction", - '50001071': "Incomplete TRN demat", - '50001072': "Incomplete PAY demat", - '50001073': "No demat APP", - '50001074': "Authorisation too old", - '50001075': "VERRes was an error message", - '50001076': "DCP amount greater than authorisation amount", - '50001077': "Details negative amount", - '50001078': "Details negative quantity", - '50001079': "Could not decode/decompress received PARes (3D-Secure)", - '50001080': "Received PARes was an erereor message from ACS (3D-Secure)", - '50001081': "Received PARes format was invalid according to the 3DS specifications (3D-Secure)", - '50001082': "PAReq/PARes reconciliation failure (3D-Secure)", - '50001084': "Maximum amount reached", - '50001087': "The transaction type requires authentication, please check with your bank.", - '50001090': "CVC missing at input, but CVC check asked", - '50001091': "ZIP missing at input, but ZIP check asked", - '50001092': "Address missing at input, but Address check asked", - '50001095': "Invalid date of birth", - '50001096': "Invalid commodity code", - '50001097': "The requested currency and brand are incompatible.", - '50001111': "Data validation error", - '50001113': "This order has already been processed", - '50001114': "Error pre-payment check page access", - '50001115': "Request not received in secure mode", - '50001116': "Unknown IP address origin", - '50001117': "NO IP address origin", - '50001118': "Pspid not found or not correct", - '50001119': "Password incorrect or disabled due to numbers of errors", - '50001120': "Invalid currency", - '50001121': "Invalid number of decimals for the currency", - '50001122': "Currency not accepted by the merchant", - '50001123': "Card type not active", - '50001124': "Number of lines don't match with number of payments", - '50001125': "Format validation error", - '50001126': "Overflow in data capture requests for the original order", - '50001127': "The original order is not in a correct status", - '50001128': "missing authorization code for unauthorized order", - '50001129': "Overflow in refunds requests", - '50001130': "Error access to original order", - '50001131': "Error access to original history item", - '50001132': "The Selected Catalog is empty", - '50001133': "Duplicate request", - '50001134': "Authentication failed, please retry or cancel.", - '50001135': "Authentication temporary unavailable, please retry or cancel.", - '50001136': "Technical problem with your browser, please retry or cancel", - '50001137': "Your bank access control server is temporary unavailable, please retry or cancel", - '50001150': "Fraud Detection, Technical error (IP not valid)", - '50001151': "Fraud detection : technical error (IPCTY unknown or error)", - '50001152': "Fraud detection : technical error (CCCTY unknown or error)", - '50001153': "Overflow in redo-authorisation requests", - '50001170': "Dynamic BIN check failed", - '50001171': "Dynamic country check failed", - '50001172': "Error in Amadeus signature", - '50001174': "Card Holder Name is too long", - '50001175': "Name contains invalid characters", - '50001176': "Card number is too long", - '50001177': "Card number contains non-numeric info", - '50001178': "Card Number Empty", - '50001179': "CVC too long", - '50001180': "CVC contains non-numeric info", - '50001181': "Expiration date contains non-numeric info", - '50001182': "Invalid expiration month", - '50001183': "Expiration date must be in the future", - '50001184': "SHA Mismatch", - '50001205': "Missing mandatory fields for billing address.", - '50001206': "Missing mandatory field date of birth.", - '50001207': "Missing required shopping basket details.", - '50001208': "Missing social security number", - '50001209': "Invalid country code", - '50001210': "Missing yearly salary", - '50001211': "Missing gender", - '50001212': "Missing email", - '50001213': "Missing IP address", - '50001214': "Missing part payment campaign ID", - '50001215': "Missing invoice number", - '50001216': "The alias must be different than the card number", - '60000001': "account number unknown", - '60000003': "not credited dd-mm-yy", - '60000005': "name/number do not correspond", - '60000007': "account number blocked", - '60000008': "specific direct debit block", - '60000009': "account number WKA", - '60000010': "administrative reason", - '60000011': "account number expired", - '60000012': "no direct debit authorisation given", - '60000013': "debit not approved", - '60000014': "double payment", - '60000018': "name/address/city not entered", - '60001001': "no original direct debit for revocation", - '60001002': "payer’s account number format error", - '60001004': "payer’s account at different bank", - '60001005': "payee’s account at different bank", - '60001006': "payee’s account number format error", - '60001007': "payer’s account number blocked", - '60001008': "payer’s account number expired", - '60001009': "payee’s account number expired", - '60001010': "direct debit not possible", - '60001011': "creditor payment not possible", - '60001012': "payer’s account number unknown WKA-number", - '60001013': "payee’s account number unknown WKA-number", - '60001014': "impermissible WKA transaction", - '60001015': "period for revocation expired", - '60001017': "reason for revocation not correct", - '60001018': "original run number not numeric", - '60001019': "payment ID incorrect", - '60001020': "amount not numeric", - '60001021': "amount zero not permitted", - '60001022': "negative amount not permitted", - '60001023': "payer and payee giro account number", - '60001025': "processing code (verwerkingscode) incorrect", - '60001028': "revocation not permitted", - '60001029': "guaranteed direct debit on giro account number", - '60001030': "NBC transaction type incorrect", - '60001031': "description too large", - '60001032': "book account number not issued", - '60001034': "book account number incorrect", - '60001035': "payer’s account number not numeric", - '60001036': "payer’s account number not eleven-proof", - '60001037': "payer’s account number not issued", - '60001039': "payer’s account number of DNB/BGC/BLA", - '60001040': "payee’s account number not numeric", - '60001041': "payee’s account number not eleven-proof", - '60001042': "payee’s account number not issued", - '60001044': "payee’s account number unknown", - '60001050': "payee’s name missing", - '60001051': "indicate payee’s bank account number instead of 3102", - '60001052': "no direct debit contract", - '60001053': "amount beyond bounds", - '60001054': "selective direct debit block", - '60001055': "original run number unknown", - '60001057': "payer’s name missing", - '60001058': "payee’s account number missing", - '60001059': "restore not permitted", - '60001060': "bank’s reference (navraaggegeven) missing", - '60001061': "BEC/GBK number incorrect", - '60001062': "BEC/GBK code incorrect", - '60001087': "book account number not numeric", - '60001090': "cancelled on request", - '60001091': "cancellation order executed", - '60001092': "cancelled instead of bended", - '60001093': "book account number is a shortened account number", - '60001094': "instructing party account number not identical with payer", - '60001095': "payee unknown GBK acceptor", - '60001097': "instructing party account number not identical with payee", - '60001099': "clearing not permitted", - '60001101': "payer’s account number not spaces", - '60001102': "PAN length not numeric", - '60001103': "PAN length outside limits", - '60001104': "track number not numeric", - '60001105': "track number not valid", - '60001106': "PAN sequence number not numeric", - '60001107': "domestic PAN not numeric", - '60001108': "domestic PAN not eleven-proof", - '60001109': "domestic PAN not issued", - '60001110': "foreign PAN not numeric", - '60001111': "card valid date not numeric", - '60001112': "book period number (boekperiodenr) not numeric", - '60001113': "transaction number not numeric", - '60001114': "transaction time not numeric", - '60001115': "transaction no valid time", - '60001116': "transaction date not numeric", - '60001117': "transaction no valid date", - '60001118': "STAN not numeric", - '60001119': "instructing party’s name missing", - '60001120': "foreign amount (bedrag-vv) not numeric", - '60001122': "rate (verrekenkoers) not numeric", - '60001125': "number of decimals (aantaldecimalen) incorrect", - '60001126': "tariff (tarifering) not B/O/S", - '60001127': "domestic costs (kostenbinnenland) not numeric", - '60001128': "domestic costs (kostenbinnenland) not higher than zero", - '60001129': "foreign costs (kostenbuitenland) not numeric", - '60001130': "foreign costs (kostenbuitenland) not higher than zero", - '60001131': "domestic costs (kostenbinnenland) not zero", - '60001132': "foreign costs (kostenbuitenland) not zero", - '60001134': "Euro record not fully filled in", - '60001135': "Client currency incorrect", - '60001136': "Amount NLG not numeric", - '60001137': "Amount NLG not higher than zero", - '60001138': "Amount NLG not equal to Amount", - '60001139': "Amount NLG incorrectly converted", - '60001140': "Amount EUR not numeric", - '60001141': "Amount EUR not greater than zero", - '60001142': "Amount EUR not equal to Amount", - '60001143': "Amount EUR incorrectly converted", - '60001144': "Client currency not NLG", - '60001145': "rate euro-vv (Koerseuro-vv) not numeric", - '60001146': "comma rate euro-vv (Kommakoerseuro-vv) incorrect", - '60001147': "acceptgiro distributor not valid", - '60001148': "Original run number and/or BRN are missing", - '60001149': "Amount/Account number/ BRN different", - '60001150': "Direct debit already revoked/restored", - '60001151': "Direct debit already reversed/revoked/restored", - '60001153': "Payer’s account number not known", -} - -DATA_VALIDATION_ERROR = '50001111' - - -def retryable(error): - return error in [ - '0020001001', '0020001002', '0020001003', '0020001004', '0020001005', - '0020001006', '0020001007', '0020001008', '0020001009', '0020001010', - '30001010', '30001011', '30001015', - '30001057', '30001058', - '30001998', '30001999', - #'30611001', # amount exceeds card limit - '30961001', - '40001001', '40001002', '40001003', '40001004', '40001005', - '40001006', '40001007', '40001008', '40001009', '40001010', - '40001012', - '40001018', '40001019', '40001020', - '40001134', '40001135', '40001136', '40001137', - #'50001174', # cardholder name too long - ] diff --git a/addons/payment_acquirer/data/ogone.xml b/addons/payment_acquirer/data/ogone.xml deleted file mode 100644 index 2288ccc0b6e..00000000000 --- a/addons/payment_acquirer/data/ogone.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - ogone - - test - pinky - OOAPI - R!ci/6Nu8a - tINY4Yv14789gUix1130 - tINYj885Tfvd4P471464 - - - - diff --git a/addons/payment_acquirer/data/paypal.xml b/addons/payment_acquirer/data/paypal.xml deleted file mode 100644 index eab754a046c..00000000000 --- a/addons/payment_acquirer/data/paypal.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - paypal - - test - https://www.sandbox.paypal.com/cgi-bin/webscr - tde+paypal-facilitator@openerp.com - 'tde+paypal-facilitator_api1.openerp.com - - - - diff --git a/addons/payment_acquirer/models/__init__.py b/addons/payment_acquirer/models/__init__.py index fdedec55fe0..0ce2de595b2 100644 --- a/addons/payment_acquirer/models/__init__.py +++ b/addons/payment_acquirer/models/__init__.py @@ -20,5 +20,3 @@ ############################################################################## import payment_acquirer -import paypal -import ogone diff --git a/addons/payment_acquirer/models/ogone.py b/addons/payment_acquirer/models/ogone.py deleted file mode 100644 index 1c7296e580f..00000000000 --- a/addons/payment_acquirer/models/ogone.py +++ /dev/null @@ -1,397 +0,0 @@ -# -*- coding: utf-'8' "-*-" -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2013-Today OpenERP SA (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from hashlib import sha1 -import logging -from lxml import etree, objectify -from pprint import pformat -# import requests -import time -from urllib import urlencode -import urllib2 -# import urlparse - -from openerp.addons.payment_acquirer.data import ogone -from openerp.addons.payment_acquirer.controllers.main import OgoneController -from openerp.addons.payment_acquirer.models.payment_acquirer import ValidationError -from openerp.osv import osv, fields -from openerp.tools import float_round - -_logger = logging.getLogger(__name__) - - -class PaymentAcquirerOgone(osv.Model): - _inherit = 'payment.acquirer' - - def _get_ogone_urls(self, cr, uid, ids, name, args, context=None): - """ Ogone URLS: - - - standard order: POST address for form-based - - @TDETODO: complete me - """ - res = {} - for acquirer in self.browse(cr, uid, ids, context=context): - qualif = acquirer.env - res[acquirer.id] = { - 'ogone_standard_order_url': 'https://secure.ogone.com/ncol/%s/orderstandard.asp' % qualif, - 'ogone_direct_order_url': 'https://secure.ogone.com/ncol/%s/orderdirect.asp' % qualif, - 'ogone_direct_query_url': 'https://secure.ogone.com/ncol/%s/querydirect.asp' % qualif, - 'ogone_afu_agree_url': 'https://secure.ogone.com/ncol/%s/AFU_agree.asp' % qualif, - } - return res - - _columns = { - 'ogone_pspid': fields.char( - 'PSPID', required_if_provider='ogone'), - 'ogone_userid': fields.char( - 'API User id', required_if_provider='ogone'), - 'ogone_password': fields.char( - 'Password', required_if_provider='ogone'), - 'ogone_shakey_in': fields.char( - 'SHA Key IN', size=32, required_if_provider='ogone'), - 'ogone_shakey_out': fields.char( - 'SHA Key OUT', size=32, required_if_provider='ogone'), - # store ogone contact URLs -> not necessary IMHO - 'ogone_standard_order_url': fields.function( - _get_ogone_urls, type='char', multi='_get_ogone_urls', - string='Stanrd Order URL (form)'), - 'ogone_direct_order_url': fields.function( - _get_ogone_urls, type='char', multi='_get_ogone_urls', - string='Direct Order URL (2)'), - 'ogone_direct_query_url': fields.function( - _get_ogone_urls, type='char', multi='_get_ogone_urls', - string='Direct Query URL'), - 'ogone_afu_agree_url': fields.function( - _get_ogone_urls, type='char', multi='_get_ogone_urls', - string='AFU Agree URL'), - } - - def _ogone_generate_shasign(self, acquirer, inout, values): - """ Generate the shasign for incoming or outgoing communications. - - :param browse acquirer: the payment.acquirer browse record. It should - have a shakey in shaky out - :param string inout: 'in' (openerp contacting ogone) or 'out' (ogone - contacting openerp). In this last case only some - fields should be contained (see e-Commerce basic) - :param dict values: transaction values - - :return string: shasign - """ - assert inout in ('in', 'out') - assert acquirer.name == 'ogone' - key = getattr(acquirer, 'ogone_shakey_' + inout) - - def filter_key(key): - if inout == 'in': - return True - else: - keys = "ORDERID CURRENCY AMOUNT PM ACCEPTANCE STATUS CARDNO ALIAS ED CN TRXDATE PAYID NCERROR BRAND ECI IP COMPLUS".split() - return key.upper() in keys - - items = sorted((k.upper(), v) for k, v in values.items()) - sign = ''.join('%s=%s%s' % (k, v, key) for k, v in items if v and filter_key(k)) - shasign = sha1(sign).hexdigest() - return shasign - - def ogone_form_generate_values(self, cr, uid, id, reference, amount, currency, partner_id=False, partner_values=None, tx_custom_values=None, context=None): - if partner_values is None: - partner_values = {} - base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url') - acquirer = self.browse(cr, uid, id, context=context) - partner = None - if partner_id: - partner = self.pool['res.partner'].browse(cr, uid, partner_id, context=context) - tx_values = { - 'PSPID': acquirer.ogone_pspid, - 'ORDERID': reference, - 'AMOUNT': '%d' % int(float_round(amount, 2) * 100), - 'CURRENCY': currency and currency.name or 'EUR', - 'LANGUAGE': partner and partner.lang or partner_values.get('lang', ''), - 'CN': partner and partner.name or partner_values.get('name', ''), - 'EMAIL': partner and partner.email or partner_values.get('email', ''), - 'OWNERZIP': partner and partner.zip or partner_values.get('zip', ''), - 'OWNERADDRESS': partner and ' '.join((partner.street, partner.street2)).strip() or ' '.join((partner_values.get('street', ''), partner_values.get('street2', ''))).strip(), - 'OWNERTOWN': partner and partner.city or partner_values.get('city', ''), - 'OWNERCTY': partner and partner.country_id and partner.country_id.name or partner_values.get('country_name', ''), - 'OWNERTELNO': partner and partner.phone or partner_values.get('phone', ''), - 'ACCEPTURL': '%s/%s' % (base_url, OgoneController._accept_url), - 'DECLINEURL': '%s/%s' % (base_url, OgoneController._decline_url), - 'EXCEPTIONURL': '%s/%s' % (base_url, OgoneController._exception_url), - 'CANCELURL': '%s/%s' % (base_url, OgoneController._cancel_url), - } - if tx_custom_values: - tx_values.update(tx_custom_values) - shasign = self._ogone_generate_shasign(acquirer, 'in', tx_values) - tx_values['SHASIGN'] = shasign - return tx_values - - -class PaymentTxOgone(osv.Model): - _inherit = 'payment.transaction' - - _columns = { - 'ogone_3ds': fields.dummy('3ds Activated'), - 'ogone_3ds_html': fields.html('3DS HTML'), - 'ogone_feedback_model': fields.char(), - 'ogone_feedback_eval': fields.char(), - 'ogone_complus': fields.char('Complus'), - } - - # -------------------------------------------------- - # FORM RELATED METHODS - # -------------------------------------------------- - - def _ogone_form_get_tx_from_shasign_out(self, cr, uid, data, context=None): - reference, pay_id, shasign = data.get('orderID'), data.get('PAYID'), data.get('SHASIGN') - if not reference or not pay_id or not shasign: - error_msg = 'Ogone: received data with missing reference (%s) or pay_id (%s) or shashign (%s)' % (reference, pay_id, shasign) - _logger.error(error_msg) - raise ValidationError(error_msg) - - # find tx -> @TDENOTE use paytid ? - tx_ids = self.pool['payment.transaction'].search(cr, uid, [('reference', '=', reference)], context=context) - if not tx_ids or len(tx_ids) > 1: - error_msg = 'Ogone: received data for reference' % (reference) - if not tx_ids: - error_msg += '; no order found' - else: - error_msg += '; multiple order found' - _logger.error(error_msg) - raise ValidationError(error_msg) - tx = self.pool['payment.transaction'].browse(cr, uid, tx_ids[0], context=context) - - # verify shasign - shasign_check = self.pool['payment.acquirer']._generate_ogone_shasign(tx.acquirer_id, 'out', data) - if shasign_check.upper() != shasign.upper(): - error_msg = 'Ogone: invalid shasign, received %s, computed %s, for data %s' % (shasign, shasign_check, data) - _logger.error(error_msg) - raise ValidationError(error_msg) - - return tx - - def ogone_form_generate_values(self, cr, uid, id, tx_custom_values=None, context=None): - tx = self.browse(cr, uid, id, context=context) - - tx_data = { - 'LANGUAGE': tx.partner_lang, - 'CN': tx.partner_name, - 'EMAIL': tx.partner_email, - 'OWNERZIP': tx.partner_zip, - 'OWNERADDRESS': tx.partner_address, - 'OWNERTOWN': tx.partner_city, - 'OWNERCTY': tx.partner_country_id and tx.partner_country_id.name or '', - 'OWNERTELNO': tx.partner_phone, - } - if tx_custom_values: - tx_data.update(tx_custom_values) - return self.pool['payment.acquirer'].ogone_form_generate_values( - cr, uid, tx.acquirer_id.id, - tx.reference, tx.amount, tx.currency_id, - tx_custom_values=tx_data, - context=context - ) - - def ogone_form_feedback(self, cr, uid, data, context=None): - print '-- ogone: ogone_form_feedback' - tx = self._ogone_get_tx_from_shasign_out(cr, uid, data, context) - if not tx: - raise ValidationError('Ogone: feedback: tx not found') - - status = int(data.get('STATUS', '0')) - print '\togone: statuts %s' % status - if status in [5, 9]: - tx.write({'state': 'done'}) - return True - else: - error = 'Ogone: feedback error: %(error_str)s\n\n%(error_code)s: %(error_msg)s' % { - 'error_str': data.get('NCERROR'), - 'error_code': data.get('NCERRORPLUS'), - 'error_msg': ogone.OGONE_ERROR_MAP.get(data.get('NCERRORPLUS')), - } - _logger.info(error) - tx.write({'state': 'error', 'state_message': error}) - return False - - # -------------------------------------------------- - # S2S RELATED METHODS - # -------------------------------------------------- - - def ogone_s2s_create_alias(self, cr, uid, id, values, context=None): - """ Purpose: create an alias via batch """ - tx = self.browse(cr, uid, id, context=context) - assert tx.type == 'server2server', 'Calling s2s dedicated method for a %s acquirer' % tx.type - alias = 'OPENERP-%d-%d' % (tx.partner_id.id, tx.id) - - expiry_date = '%s%s' % (values['expiry_date_mm'], values['expiry_date_yy'][2:]) - line = 'ADDALIAS;%(alias)s;%(holder_name)s;%(number)s;%(expiry_date)s;%(brand)s;%(pspid)s' - line = line % dict(values, alias=alias, expiry_date=expiry_date, pspid=tx.acquirer_id.ogone_pspid) - - tx_data = { - 'FILE_REFERENCE': 'OPENERP-NEW-ALIAS-%s' % time.time(), # something unique, - 'TRANSACTION_CODE': 'ATR', - 'OPERATION': 'SAL', - 'NB_PAYMENTS': 1, # even if we do not actually have any payment, ogone want it to not be 0 - 'FILE': line, - 'REPLY_TYPE': 'XML', - 'PSPID': tx.acquirer_id.ogone_pspid, - 'USERID': tx.acquirer_id.ogone_userid, - 'PSWD': tx.acquirer_id.ogone_password, - 'PROCESS_MODE': 'CHECKANDPROCESS', - } - - request = urllib2.Request(tx.acquirer_id.ogone_afu_agree_url, urlencode(tx_data)) - result = urllib2.urlopen(request).read() - - try: - tree = objectify.fromstring(result) - except etree.XMLSyntaxError: - _logger.exception('Invalid xml response from ogone') - return None - - error_code = error_str = None - if hasattr(tree, 'PARAMS_ERROR'): - error_code = tree.NCERROR.text - error_str = 'PARAMS ERROR: %s' % (tree.PARAMS_ERROR.text or '',) - else: - node = tree.FORMAT_CHECK - error_node = getattr(node, 'FORMAT_CHECK_ERROR', None) - if error_node is not None: - error_code = error_node.NCERROR.text - error_str = 'CHECK ERROR: %s' % (error_node.ERROR.text or '',) - - if error_code: - error_msg = ogone.OGONE_ERROR_MAP.get(error_code) - error = '%s\n\n%s: %s' % (error_str, error_code, error_msg) - _logger.error(error) - raise Exception(error) # TODO specific exception - - tx.write({'partner_reference': alias}) - return True - - def ogone_s2s_generate_values(self, cr, uid, id, custom_values, context=None): - tx = self.browse(cr, uid, id, context=context) - tx_data = { - 'PSPID': tx.acquirer_id.ogone_pspid, - 'USERID': tx.acquirer_id.ogone_userid, - 'PSWD': tx.acquirer_id.ogone_password, - 'OrderID': tx.reference, - 'amount': '%d' % int(float_round(tx.amount, 2) * 100), # tde check amount or str * 100 ? - 'CURRENCY': tx.currency_id.name, - 'LANGUAGE': tx.partner_lang, - 'OPERATION': 'SAL', - 'ECI': 2, # Recurring (from MOTO) - 'ALIAS': tx.partner_reference, - 'RTIMEOUT': 30, - } - if custom_values.get('ogone_cvc'): - tx_data['CVC'] = custom_values.get('ogone_cvc') - if custom_values.pop('ogone_3ds', None): - tx_data.update({ - 'FLAG3D': 'Y', # YEAH!! - }) - if custom_values.get('ogone_complus'): - tx_data['COMPLUS'] = custom_values.get('ogone_complus') - if custom_values.get('ogone_accept_url'): - pass - - shasign = self.pool['payment.acquirer']._ogone_generate_shasign(tx.acquirer_id, 'in', tx_data) - tx_data['SHASIGN'] = shasign - return tx_data - - def ogone_s2s_execute(self, cr, uid, id, values, context=None): - tx = self.browse(cr, uid, id, context=context) - - tx_data = self.ogone_s2s_generate_values(cr, uid, id, values, context=context) - _logger.info('Generated Ogone s2s data %s', pformat(tx_data)) # debug - - request = urllib2.Request(tx.acquirer_id.ogone_direct_order_url, urlencode(tx_data)) - result = urllib2.urlopen(request).read() - _logger.info('Contacted Ogone direct order; result %s', result) # debug - - tree = objectify.fromstring(result) - payid = tree.get('PAYID') - print 'payid', payid - - query_direct_data = dict( - PSPID=tx.acquirer_id.ogone_pspid, - USERID=tx.acquirer_id.ogone_userid, - PSWD=tx.acquirer_id.ogone_password, - ID=payid, - ) - query_direct_url = 'https://secure.ogone.com/ncol/%s/querydirect.asp' % (tx.acquirer_id.env,) - - tries = 2 - tx_done = False - tx_status = False - while not tx_done or tries > 0: - try: - tree = objectify.fromstring(result) - except etree.XMLSyntaxError: - # invalid response from ogone - _logger.exception('Invalid xml response from ogone') - raise - - # see https://secure.ogone.com/ncol/paymentinfos1.asp - VALID_TX = [5, 9] - WAIT_TX = [41, 50, 51, 52, 55, 56, 91, 92, 99] - PENDING_TX = [46] # 3DS HTML response - # other status are errors... - - status = tree.get('STATUS') - if status == '': - status = None - else: - status = int(status) - - if status in VALID_TX: - tx_status = True - tx_done = True - - elif status in PENDING_TX: - html = str(tree.HTML_ANSWER) - tx_data.update(ogone_3ds_html=html.decode('base64')) - tx_status = False - tx_done = True - - elif status in WAIT_TX: - time.sleep(1500) - - request = urllib2.Request(query_direct_url, urlencode(query_direct_data)) - result = urllib2.urlopen(request).read() - _logger.debug('Contacted Ogone query direct; result %s', result) - - else: - error_code = tree.get('NCERROR') - if not ogone.retryable(error_code): - error_str = tree.get('NCERRORPLUS') - error_msg = ogone.OGONE_ERROR_MAP.get(error_code) - error = 'ERROR: %s\n\n%s: %s' % (error_str, error_code, error_msg) - _logger.info(error) - raise Exception(error) - - tries = tries - 1 - - if not tx_done and tries == 0: - raise Exception('Cannot get transaction status...') - - return (tx_status, orderid, payid) diff --git a/addons/payment_acquirer/models/paypal.py b/addons/payment_acquirer/models/paypal.py deleted file mode 100644 index 7fbc0638ff6..00000000000 --- a/addons/payment_acquirer/models/paypal.py +++ /dev/null @@ -1,241 +0,0 @@ -# -*- coding: utf-'8' "-*-" -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2013-Today OpenERP SA (). -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.addons.payment_acquirer.controllers.main import PaypalController -from openerp.addons.payment_acquirer.models.payment_acquirer import ValidationError -from openerp.osv import osv, fields - -import logging -import requests -import urlparse - -_logger = logging.getLogger(__name__) - - -class AcquirerPaypal(osv.Model): - _inherit = 'payment.acquirer' - - _columns = { - 'paypal_email_id': fields.char('Email ID', required_if_provider='paypal'), - 'paypal_username': fields.char('Username', required_if_provider='paypal'), - 'paypal_password': fields.char('Password'), - 'paypal_signature': fields.char('Signature'), - 'paypal_tx_url': fields.char('Transaction URL', required_if_provider='paypal'), - 'paypal_use_dpn': fields.boolean('Use DPN'), - 'paypal_use_ipn': fields.boolean('Use IPN'), - } - - _defaults = { - 'paypal_use_dpn': True, - 'paypal_use_ipn': True, - 'paypal_tx_url': 'https://www.sandbox.paypal.com/cgi-bin/webscr', - } - - def paypal_form_generate_values(self, cr, uid, id, reference, amount, currency, partner_id=False, partner_values=None, tx_custom_values=None, context=None): - if partner_values is None: - partner_values = {} - base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url') - acquirer = self.browse(cr, uid, id, context=context) - partner = None - if partner_id: - partner = self.pool['res.partner'].browse(cr, uid, partner_id, context=context) - tx_values = { - 'cmd': '_xclick', - 'business': acquirer.paypal_email_id, - 'item_name': reference, - 'item_number': reference, - 'amount': amount, - 'currency_code': currency and currency.name or 'EUR', - 'address1': partner and ' '.join((partner.street, partner.street2)).strip() or ' '.join((partner_values.get('street', ''), partner_values.get('street2', ''))).strip(), - 'city': partner and partner.city or partner_values.get('city', ''), - 'country': partner and partner.country_id and partner.country_id.name or partner_values.get('country_name', ''), - 'email': partner and partner.email or partner_values.get('email', ''), - 'zip': partner and partner.zip or partner_values.get('zip', ''), - 'first_name': partner and partner.name or partner_values.get('name', '').split()[-1:], - 'last_name': partner and partner.name or partner_values.get('name', '').split()[:-1], - 'return': '%s/%s' % (base_url, PaypalController._return_url), - 'notify_url': '%s/%s' % (base_url, PaypalController._notify_url), - 'cancel_return': '%s/%s' % (base_url, PaypalController._cancel_url), - } - if tx_custom_values: - tx_values.update(tx_custom_values) - return tx_values - - -class TxPaypal(osv.Model): - _inherit = 'payment.transaction' - - _columns = { - 'paypal_txn_id': fields.char('Transaction ID'), - } - - # -------------------------------------------------- - # FORM RELATED METHODS - # -------------------------------------------------- - - def paypal_form_generate_values(self, cr, uid, id, tx_custom_values=None, context=None): - tx = self.browse(cr, uid, id, context=context) - - tx_data = { - 'item_name': tx.name, - 'first_name': tx.partner_name and tx.partner_name.split()[-1:], - 'last_name': tx.partner_name and tx.partner_name.split()[:-1], - 'email': tx.partner_email, - 'zip': tx.partner_zip, - 'address1': tx.partner_address, - 'city': tx.partner_city, - 'country': tx.partner_country_id and tx.partner_country_id.name or '', - } - if tx_custom_values: - tx_data.update(tx_custom_values) - return self.pool['payment.acquirer'].paypal_form_generate_values( - cr, uid, tx.acquirer_id.id, - tx.reference, tx.amount, tx.currency_id, - tx_custom_values=tx_data, - context=context - ) - - - - def validate_paypal_notification(self, cr, uid, url, context=None): - parsed_url = urlparse.urlparse(url) - query_parameters = parsed_url.query - parameters = urlparse.parse_qs(query_parameters) - - invalid_parameters = [] - - # check tx effectively exists - txn_id = parameters.get('txn_id')[0] - tx_ids = self.search(cr, uid, [('paypal_txn_id', '=', txn_id)], context=context) - if not tx_ids: - _logger.warning( - 'Received a notification from Paypal for a tx %s that does not exists in database.' % - txn_id - ) - return False - elif len(tx_ids) > 1: - _logger.warning( - 'Received a notification from Paypal for a tx %s that is duplicated in database.' % - txn_id - ) - - tx = self.browse(cr, uid, tx_ids[0], context=context) - - if parameters.get('notify_version')[0] != '2.6': - _logger.warning( - 'Received a notification from Paypal with version %s instead of 2.6. This could lead to issues when managing it.' % - parameters.get('notify_version') - ) - if parameters.get('test_ipn')[0]: - _logger.warning( - 'Received a notification from Paypal using sandbox' - ), - # check transaction - if parameters.get('payment_status')[0] != 'Completed': - invalid_parameters.append(('payment_status', 'Completed')) - # check what is buyed - if parameters.get('mc_gross')[0] != tx.amount: - invalid_parameters.append(('mc_gross', tx.amount)) - if parameters.get('mc_currency')[0] != tx.currency_id.name: - invalid_parameters.append(('mc_currency', tx.currency_id.name)) - # if parameters.get('payment_fee') != tx.payment_fee: - # invalid_parameters.append(('payment_fee', tx.payment_fee)) - # if parameters.get('quantity') != tx.quantity: - # invalid_parameters.append(('mc_currency', tx.quantity)) - # if parameters.get('shipping') != tx.shipping: - # invalid_parameters.append(('shipping', tx.shipping)) - # check buyer - # if parameters.get('payer_id') != tx.payer_id: - # invalid_parameters.append(('mc_gross', tx.payer_id)) - # if parameters.get('payer_email') != tx.payer_email: - # invalid_parameters.append(('payer_email', tx.payer_email)) - # check seller - # if parameters.get('receiver_email') != tx.receiver_email: - # invalid_parameters.append(('receiver_email', tx.receiver_email)) - # if parameters.get('receiver_id') != tx.receiver_id: - # invalid_parameters.append(('receiver_id', tx.receiver_id)) - - if not invalid_parameters: - self.write(cr, uid, [tx.id], { - 'payment_type': parameters.get('payment_type')[0], - 'date_validate': parameters.get('payment_date', [fields.datetime.now()])[0], - 'txn_type': parameters.get('express_checkout')[0], - }, context=context) - return tx.id - else: - _warn_message = 'The following transaction parameters are incorrect:\n' - for item in invalid_parameters: - _warn_message += '\t%s: received %s instead of %s\n' % (item[0], parameters.get(item[0])[0], item[1]) - _logger.warning(_warn_message) - - return False - - def create_paypal_command(self, cr, uid, cmd, parameters): - parameters.update(cmd=cmd) - return requests.post(self._paypal_url, data=parameters) - - def _validate_paypal(self, cr, uid, ids, context=None): - res = [] - for tx in self.browse(cr, uid, ids, context=context): - parameters = {} - parameters.update( - cmd='_notify-validate', - business='tdelavallee-facilitator@gmail.com', - item_name="%s %s" % ('cacapoutch', tx.reference), - item_number=tx.reference, - amount=tx.amount, - currency_code=tx.currency_id.name, - ) - print '\t', parameters - # paypal_url = "https://www.paypal.com/cgi-bin/webscr" - paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr" - resp = requests.post(paypal_url, data=parameters) - print resp - print resp.url - print resp.text - response = urlparse.parse_qsl(resp) - print response - # transaction's unique id - # response["txn_id"] - - # "Failed", "Reversed", "Refunded", "Canceled_Reversal", "Denied" - status = "refused" - retry_time = False - - if response["payment_status"] == "Voided": - status = "refused" - elif response["payment_status"] in ("Completed", "Processed") and response["item_number"] == tx.reference and response["mc_gross"] == tx.amount: - status = "validated" - elif response["payment_status"] in ("Expired", "Pending"): - status = "pending" - retry_time = 60 - - res.append( - (status, retry_time, "payment_status=%s&pending_reason=%s&reason_code=%s" % ( - response["payment_status"], - response.get("pending_reason"), - response.get("reason_code"))) - ) - return response - - def _transaction_feedback_paypal(self, **values): - print values - return True diff --git a/addons/payment_acquirer/tests/__init__.py b/addons/payment_acquirer/tests/__init__.py index ffdb032ee0f..8d0bb0195bf 100644 --- a/addons/payment_acquirer/tests/__init__.py +++ b/addons/payment_acquirer/tests/__init__.py @@ -19,10 +19,4 @@ # ############################################################################## -from . import test_payment_acquirer - -checks = [ - test_payment_acquirer, -] - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: +import common diff --git a/addons/payment_acquirer/tests/common.py b/addons/payment_acquirer/tests/common.py index a99a5d1a610..df368f5b1dd 100644 --- a/addons/payment_acquirer/tests/common.py +++ b/addons/payment_acquirer/tests/common.py @@ -22,10 +22,10 @@ from openerp.tests import common -class TestPaymentAcquirer(common.TransactionCase): +class PaymentAcquirerCommon(common.TransactionCase): def setUp(self): - super(TestPaymentAcquirer, self).setUp() + super(PaymentAcquirerCommon, self).setUp() self.payment_acquirer = self.registry('payment.acquirer') self.payment_transaction = self.registry('payment.transaction') diff --git a/addons/payment_acquirer/tests/test_payment_acquirer.py b/addons/payment_acquirer/tests/test_payment_acquirer.py deleted file mode 100644 index 0de5813412e..00000000000 --- a/addons/payment_acquirer/tests/test_payment_acquirer.py +++ /dev/null @@ -1,272 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Business Applications -# Copyright (c) 2012-TODAY OpenERP S.A. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as -# published by the Free Software Foundation, either version 3 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################## - -from openerp.addons.payment_acquirer.controllers.main import OgoneController -from openerp.addons.payment_acquirer.tests.common import TestPaymentAcquirer -from openerp.osv.orm import except_orm - -from lxml import objectify -# import requests -# import urlparse - - -class BasicPayment(TestPaymentAcquirer): - - def test_10_paypal_basic(self): - pass - - def test_11_paypal_form(self): - cr, uid = self.cr, self.uid - context = {} - base_url = self.registry('ir.config_parameter').get_param(cr, uid, 'web.base.url') - # ogone_url = self.payment_acquirer._get_ogone_urls(cr, uid, [ogone_id], None, None)[ogone_id]['ogone_standard_order_url'] - - model, paypal_view_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'payment_acquirer', 'paypal_acquirer_button') - - # forgot some mandatory fields: should crash - with self.assertRaises(except_orm): - paypal_id = self.payment_acquirer.create( - cr, uid, { - 'name': 'paypal', - 'env': 'test', - 'view_template_id': paypal_view_id, - 'paypal_email_id': 'tde+paypal-facilitator@openerp.com', - }, context=context - ) - # tde+buyer@openerp.com - - # create a new paypal account - paypal_id = self.payment_acquirer.create( - cr, uid, { - 'name': 'paypal', - 'env': 'test', - 'view_template_id': paypal_view_id, - 'paypal_email_id': 'tde+paypal-facilitator@openerp.com', - 'paypal_username': 'tde+paypal-facilitator_api1.openerp.com', - }, context=context - ) - # verify acquirer data - paypal = self.payment_acquirer.browse(cr, uid, paypal_id, context) - self.assertEqual(paypal.env, 'test', 'test without test env') - - # render the button - res = self.payment_acquirer.render( - cr, uid, paypal_id, - 'test_ref0', 0.01, self.currency_euro, - partner_id=None, - partner_values=self.buyer_values, - context=context) - print res - - # # check some basic paypal methods - # res = self.payment_transaction.validate_paypal_notification( - # cr, uid, - # 'http://localhost/payment?mc_gross=19.95&protection_eligibility=Eligible&address_status=confirmed&payer_id=LPLWNMTBWMFAY&tax=0.00&address_street=1+Main+St&payment_date=20%3A12%3A59+Jan+13%2C+2009+PST&payment_status=Completed&charset=windows-1252&address_zip=95131&first_name=Test&mc_fee=0.88&address_country_code=US&address_name=Test+User¬ify_version=2.6&custom=&payer_status=verified&address_country=United+States&address_city=San+Jose&quantity=1&verify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtf&payer_email=gpmac_1231902590_per%40paypal.com&txn_id=61E67681CH3238416&payment_type=instant&last_name=User&address_state=CA&receiver_email=gpmac_1231902686_biz%40paypal.com&payment_fee=0.88&receiver_id=S8XGHLYDW9T3S&txn_type=express_checkout&item_name=&mc_currency=USD&item_number=&residence_country=US&test_ipn=1&handling_amount=0.00&transaction_subject=&payment_gross=19.95&shipping=0.00') - # self.assertEqual(res, False, 'payment: paypal validation on a txn_id that does not exists should return False') - - # txn_id = self.payment_transaction.create( - # cr, uid, { - # 'amount': 0.01, - # 'acquirer_id': paypal_id, - # 'currency_id': currency_id, - # 'reference': 'test_reference', - # 'paypal_txn_id': '61E67681CH3238416', - # }, context=context - # ) - # res = self.payment_transaction.validate_paypal_notification( - # cr, uid, - # 'http://localhost/payment?mc_gross=19.95&protection_eligibility=Eligible&address_status=confirmed&payer_id=LPLWNMTBWMFAY&tax=0.00&address_street=1+Main+St&payment_date=20%3A12%3A59+Jan+13%2C+2009+PST&payment_status=Completed&charset=windows-1252&address_zip=95131&first_name=Test&mc_fee=0.88&address_country_code=US&address_name=Test+User¬ify_version=2.6&custom=&payer_status=verified&address_country=United+States&address_city=San+Jose&quantity=1&verify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtf&payer_email=gpmac_1231902590_per%40paypal.com&txn_id=61E67681CH3238416&payment_type=instant&last_name=User&address_state=CA&receiver_email=gpmac_1231902686_biz%40paypal.com&payment_fee=0.88&receiver_id=S8XGHLYDW9T3S&txn_type=express_checkout&item_name=&mc_currency=USD&item_number=&residence_country=US&test_ipn=1&handling_amount=0.00&transaction_subject=&payment_gross=19.95&shipping=0.00') - # print res - - # # user pays using Paypal - # resp = self.payment_transaction.create_paypal_command( - # cr, uid, cmd='_xclick', parameters={ - # 'business': 'tdelavallee-facilitator@gmail.com', - # 'amount': 50, - # 'item_name': 'test_item', - # 'quantity': 1, - # 'currency_code': 'USD', - # 'return': 'http://www.example.com', - # }) - # print resp - # print resp.url - # print resp.text - - # self.payment_transaction.validate(cr, uid, [tx_id], context=context) - - def test_20_ogone_form(self): - cr, uid = self.cr, self.uid - context = {} - base_url = self.registry('ir.config_parameter').get_param(cr, uid, 'web.base.url') - # ogone_url = self.payment_acquirer._get_ogone_urls(cr, uid, [ogone_id], None, None)[ogone_id]['ogone_standard_order_url'] - - model, ogone_view_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'payment_acquirer', 'ogone_acquirer_button') - - # create a new ogone account - ogone_id = self.payment_acquirer.create( - cr, uid, { - 'name': 'ogone', - 'env': 'test', - 'view_template_id': ogone_view_id, - 'ogone_pspid': 'pinky', - 'ogone_userid': 'OOAPI', - 'ogone_password': 'R!ci/6Nu8a', - 'ogone_shakey_in': 'tINY4Yv14789gUix1130', - 'ogone_shakey_out': 'tINYj885Tfvd4P471464', - }, context=context - ) - # verify acquirer data - ogone = self.payment_acquirer.browse(cr, uid, ogone_id, context) - self.assertEqual(ogone.env, 'test', 'test without test env') - - form_values = { - 'PSPID': 'pinky', - 'ORDERID': 'test_ref0', - 'AMOUNT': '1', - 'CURRENCY': 'EUR', - 'LANGUAGE': 'en_US', - 'CN': 'Norbert Buyer', - 'EMAIL': 'norbert.buyer@example.com', - 'OWNERZIP': '1000', - 'OWNERADDRESS': 'Huge Street 2/543', - 'OWNERCTY': 'Belgium', - 'OWNERTOWN': 'Sin City', - 'OWNERTELNO': '0032 12 34 56 78', - 'SHASIGN': 'ea74bb42d4f25746279cdd44a737aaddc71e7f9f', - 'ACCEPTURL': '%s/%s' % (base_url, OgoneController._accept_url), - 'DECLINEURL': '%s/%s' % (base_url, OgoneController._decline_url), - 'EXCEPTIONURL': '%s/%s' % (base_url, OgoneController._exception_url), - 'CANCELURL': '%s/%s' % (base_url, OgoneController._cancel_url), - } - - # render the button - res = self.payment_acquirer.render( - cr, uid, ogone_id, - 'test_ref0', 0.01, self.currency_euro, - partner_id=None, - partner_values=self.buyer_values, - context=context) - # check form result - tree = objectify.fromstring(res) - self.assertEqual(tree.get('action'), 'https://secure.ogone.com/ncol/test/orderstandard.asp', 'ogone: wrong form POST url') - for form_input in tree.input: - if form_input.get('name') in ['submit']: - continue - self.assertEqual( - form_input.get('value'), - form_values[form_input.get('name')], - 'ogone: wrong value for form: received %s instead of %s' % (form_input.get('value'), form_values[form_input.get('name')]) - ) - # resp = requests.post(tree.get('action'), data=form_values) - - # create a new draft tx - tx_id = self.payment_transaction.create( - cr, uid, { - 'amount': 0.01, - 'acquirer_id': ogone_id, - 'currency_id': self.currency_euro_id, - 'reference': 'test_ref0', - 'partner_id': self.buyer_id, - }, context=context - ) - # render the button - res = self.payment_acquirer.render( - cr, uid, ogone_id, - 'test_ref0', 0.01, self.currency_euro, - tx_id=tx_id, - partner_id=None, - partner_values=self.buyer_values, - context=context) - # check form result - tree = objectify.fromstring(res) - self.assertEqual(tree.get('action'), 'https://secure.ogone.com/ncol/test/orderstandard.asp', 'ogone: wrong form POST url') - for form_input in tree.input: - if form_input.get('name') in ['submit']: - continue - self.assertEqual( - form_input.get('value'), - form_values[form_input.get('name')], - 'ogone: wrong value for form input %s: received %s instead of %s' % (form_input.get('name'), form_input.get('value'), form_values[form_input.get('name')]) - ) - - def test_21_ogone_s2s(self): - cr, uid = self.cr, self.uid - context = {} - - model, ogone_view_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'payment_acquirer', 'ogone_acquirer_button') - - # create a new ogone account - ogone_id = self.payment_acquirer.create( - cr, uid, { - 'name': 'ogone', - 'env': 'test', - 'view_template_id': ogone_view_id, - 'ogone_pspid': 'pinky', - 'ogone_userid': 'OOAPI', - 'ogone_password': 'R!ci/6Nu8a', - 'ogone_shakey_in': 'tINY4Yv14789gUix1130', - 'ogone_shakey_out': 'tINYj885Tfvd4P471464', - }, context=context - ) - - # create a new draft tx - tx_id = self.payment_transaction.create( - cr, uid, { - 'amount': 0.01, - 'acquirer_id': ogone_id, - 'currency_id': self.currency_euro_id, - 'reference': 'test_ogone_0', - 'partner_id': self.buyer_id, - 'type': 'server2server', - }, context=context - ) - - res = self.payment_transaction.ogone_s2s_create_alias( - cr, uid, tx_id, { - 'expiry_date_mm': '01', - 'expiry_date_yy': '2015', - 'holder_name': 'Norbert Poilu', - 'number': '4000000000000002', - 'brand': 'VISA', - }, context=context) - print res - - res = self.payment_transaction.ogone_s2s_execute(cr, uid, tx_id, {}, context=context) - print res - - -# { -# 'orderID': u'reference', -# 'STATUS': u'9', -# 'CARDNO': u'XXXXXXXXXXXX0002', -# 'PAYID': u'24998692', -# 'CN': u'Norbert Poilu', -# 'NCERROR': u'0', -# 'TRXDATE': u'11/05/13', -# 'IP': u'85.201.233.72', -# 'BRAND': u'VISA', -# 'ACCEPTANCE': u'test123', -# 'currency': u'EUR', -# 'amount': u'1.95', -# 'SHASIGN': u'EFDC56879EF7DE72CCF4B397076B5C9A844CB0FA', -# 'ED': u'0314', -# 'PM': u'CreditCard' -# } diff --git a/addons/payment_acquirer/views/ogone.xml b/addons/payment_acquirer/views/ogone.xml deleted file mode 100644 index 0945ebf22d8..00000000000 --- a/addons/payment_acquirer/views/ogone.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - diff --git a/addons/payment_acquirer/views/paypal.xml b/addons/payment_acquirer/views/paypal.xml deleted file mode 100644 index 105a8595057..00000000000 --- a/addons/payment_acquirer/views/paypal.xml +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - diff --git a/addons/payment_acquirer_ogone/__init__.py b/addons/payment_acquirer_ogone/__init__.py new file mode 100644 index 00000000000..dde6f25c728 --- /dev/null +++ b/addons/payment_acquirer_ogone/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import models +import controllers diff --git a/addons/payment_acquirer_ogone/__openerp__.py b/addons/payment_acquirer_ogone/__openerp__.py new file mode 100644 index 00000000000..9583319c4e5 --- /dev/null +++ b/addons/payment_acquirer_ogone/__openerp__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + 'name': 'Ogone Payment Acquirer', + 'category': 'Hidden', + 'summary': 'Payment acquirer: Ogone', + 'version': '0.1', + 'description': """Ogone Payment Acquirer""", + 'author': 'OpenERP SA', + 'depends': ['payment_acquirer'], + 'data': [ + 'views/ogone.xml', + 'data/ogone.xml', + ], + 'installable': True, +} diff --git a/addons/payment_acquirer_ogone/controllers/__init__.py b/addons/payment_acquirer_ogone/controllers/__init__.py new file mode 100644 index 00000000000..039d9715fab --- /dev/null +++ b/addons/payment_acquirer_ogone/controllers/__init__.py @@ -0,0 +1 @@ +import main \ No newline at end of file diff --git a/addons/payment_acquirer_ogone/controllers/main.py b/addons/payment_acquirer_ogone/controllers/main.py new file mode 100644 index 00000000000..8633ba30f12 --- /dev/null +++ b/addons/payment_acquirer_ogone/controllers/main.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.addons.web import http +from openerp.addons.web.http import request +# from openerp.addons.payment_acquirer.models.payment_acquirer import ValidationError +from openerp.addons.website.models import website + +import logging +import requests +from urllib import urlencode + +_logger = logging.getLogger(__name__) + + +class OgoneController(http.Controller): + _accept_url = '/payment/ogone/test/accept' + _decline_url = '/payment/ogone/test/decline' + _exception_url = '/payment/ogone/test/exception' + _cancel_url = '/payment/ogone/test/cancel' + + @website.route([ + '/payment/ogone/feedback', '/payment/ogone/test/accept', + '/payment/ogone/decline', '/payment/ogone/test/decline', + '/payment/ogone/exception', '/payment/ogone/test/exception', + '/payment/ogone/cancel', '/payment/ogone/test/cancel', + ], type='http', auth='admin') + def feedback(self, **post): + cr, uid, context = request.cr, request.uid, request.context + Payment = request.registry['payment.transaction'] + print 'Entering ogone feedback with', post + + res = Payment.tx_ogone_feedback(cr, uid, post, context) + print res + return '' diff --git a/addons/payment_acquirer_ogone/data/__init__.py b/addons/payment_acquirer_ogone/data/__init__.py new file mode 100644 index 00000000000..61f4cbeacf3 --- /dev/null +++ b/addons/payment_acquirer_ogone/data/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import ogone diff --git a/addons/payment_acquirer_ogone/data/ogone.py b/addons/payment_acquirer_ogone/data/ogone.py new file mode 100644 index 00000000000..3a2500ee24b --- /dev/null +++ b/addons/payment_acquirer_ogone/data/ogone.py @@ -0,0 +1,497 @@ +# -*- coding: utf-8 -*- + +OGONE_ERROR_MAP = { + '0020001001': "Authorization failed, please retry", + '0020001002': "Authorization failed, please retry", + '0020001003': "Authorization failed, please retry", + '0020001004': "Authorization failed, please retry", + '0020001005': "Authorization failed, please retry", + '0020001006': "Authorization failed, please retry", + '0020001007': "Authorization failed, please retry", + '0020001008': "Authorization failed, please retry", + '0020001009': "Authorization failed, please retry", + '0020001010': "Authorization failed, please retry", + '0030001999': "Our payment system is currently under maintenance, please try later", + '0050001005': "Expiry date error", + '0050001007': "Requested Operation code not allowed", + '0050001008': "Invalid delay value", + '0050001010': "Input date in invalid format", + '0050001013': "Unable to parse socket input stream", + '0050001014': "Error in parsing stream content", + '0050001015': "Currency error", + '0050001016': "Transaction still posted at end of wait", + '0050001017': "Sync value not compatible with delay value", + '0050001019': "Transaction duplicate of a pre-existing transaction", + '0050001020': "Acceptation code empty while required for the transaction", + '0050001024': "Maintenance acquirer differs from original transaction acquirer", + '0050001025': "Maintenance merchant differs from original transaction merchant", + '0050001028': "Maintenance operation not accurate for the original transaction", + '0050001031': "Host application unknown for the transaction", + '0050001032': "Unable to perform requested operation with requested currency", + '0050001033': "Maintenance card number differs from original transaction card number", + '0050001034': "Operation code not allowed", + '0050001035': "Exception occurred in socket input stream treatment", + '0050001036': "Card length does not correspond to an acceptable value for the brand", + '0050001036': "Card length does not correspond to an acceptable value for the brand", + '0050001068': "A technical problem occurred, please contact helpdesk", + '0050001069': "Invalid check for CardID and Brand", + '0050001070': "A technical problem occurred, please contact helpdesk", + '0050001116': "Unknown origin IP", + '0050001117': "No origin IP detected", + '0050001118': "Merchant configuration problem, please contact support", + '10001001': "Communication failure", + '10001002': "Communication failure", + '10001003': "Communication failure", + '10001004': "Communication failure", + '10001005': "Communication failure", + '20001001': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", + '20001002': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", + '20001003': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", + '20001004': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", + '20001005': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", + '20001006': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", + '20001007': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", + '20001008': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", + '20001009': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", + '20001010': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", + '20001101': "A technical problem occurred, please contact helpdesk", + '20001105': "We received an unknown status for the transaction. We will contact your acquirer and update the status of the transaction within one working day. Please check the status later.", + '20001111': "A technical problem occurred, please contact helpdesk", + '20002001': "Origin for the response of the bank can not be checked", + '20002002': "Beneficiary account number has been modified during processing", + '20002003': "Amount has been modified during processing", + '20002004': "Currency has been modified during processing", + '20002005': "No feedback from the bank server has been detected", + '30001001': "Payment refused by the acquirer", + '30001002': "Duplicate request", + '30001010': "A technical problem occurred, please contact helpdesk", + '30001011': "A technical problem occurred, please contact helpdesk", + '30001012': "Card black listed - Contact acquirer", + '30001015': "Your merchant's acquirer is temporarily unavailable, please try later or choose another payment method.", + '30001051': "A technical problem occurred, please contact helpdesk", + '30001054': "A technical problem occurred, please contact helpdesk", + '30001057': "Your merchant's acquirer is temporarily unavailable, please try later or choose another payment method.", + '30001058': "Your merchant's acquirer is temporarily unavailable, please try later or choose another payment method.", + '30001060': "Aquirer indicates that a failure occured during payment processing", + '30001070': "RATEPAY Invalid Response Type (Failure)", + '30001071': "RATEPAY Missing Mandatory status code field (failure)", + '30001072': "RATEPAY Missing Mandatory Result code field (failure)", + '30001073': "RATEPAY Response parsing Failed", + '30001090': "CVC check required by front end and returned invalid by acquirer", + '30001091': "ZIP check required by front end and returned invalid by acquirer", + '30001092': "Address check required by front end and returned as invalid by acquirer.", + '30001100': "Unauthorized buyer's country", + '30001101': "IP country <> card country", + '30001102': "Number of different countries too high", + '30001103': "unauthorized card country", + '30001104': "unauthorized ip address country", + '30001105': "Anonymous proxy", + '30001110': "If the problem persists, please contact Support, or go to paysafecard's card balance page (https://customer.cc.at.paysafecard.com/psccustomer/GetWelcomePanelServlet?language=en) to see when the amount reserved on your card will be available again.", + '30001120': "IP address in merchant's black list", + '30001130': "BIN in merchant's black list", + '30001131': "Wrong BIN for 3xCB", + '30001140': "Card in merchant's card blacklist", + '30001141': "Email in blacklist", + '30001142': "Passenger name in blacklist", + '30001143': "Card holder name in blacklist", + '30001144': "Passenger name different from owner name", + '30001145': "Time to departure too short", + '30001149': "Card Configured in Card Supplier Limit for another relation (CSL)", + '30001150': "Card not configured in the system for this customer (CSL)", + '30001151': "REF1 not allowed for this relationship (Contract number", + '30001152': "Card/Supplier Amount limit reached (CSL)", + '30001153': "Card not allowed for this supplier (Date out of contract bounds)", + '30001154': "You have reached the usage limit allowed", + '30001155': "You have reached the usage limit allowed", + '30001156': "You have reached the usage limit allowed", + '30001157': "Unauthorized IP country for itinerary", + '30001158': "email usage limit reached", + '30001159': "Unauthorized card country/IP country combination", + '30001160': "Postcode in highrisk group", + '30001161': "generic blacklist match", + '30001162': "Billing Address is a PO Box", + '30001180': "maximum scoring reached", + '30001997': "Authorization canceled by simulation", + '30001998': "A technical problem occurred, please try again.", + '30001999': "Your merchant's acquirer is temporarily unavailable, please try later or choose another payment method.", + '30002001': "Payment refused by the financial institution", + '30002001': "Payment refused by the financial institution", + '30021001': "Call acquirer support call number.", + '30022001': "Payment must be approved by the acquirer before execution.", + '30031001': "Invalid merchant number.", + '30041001': "Retain card.", + '30051001': "Authorization declined", + '30071001': "Retain card - special conditions.", + '30121001': "Invalid transaction", + '30131001': "Invalid amount", + '30131002': "You have reached the total amount allowed", + '30141001': "Invalid card number", + '30151001': "Unknown acquiring institution.", + '30171001': "Payment method cancelled by the buyer", + '30171002': "The maximum time allowed is elapsed.", + '30191001': "Try again later.", + '30201001': "A technical problem occurred, please contact helpdesk", + '30301001': "Invalid format", + '30311001': "Unknown acquirer ID.", + '30331001': "Card expired.", + '30341001': "Suspicion of fraud.", + '30341002': "Suspicion of fraud (3rdMan)", + '30341003': "Suspicion of fraud (Perseuss)", + '30341004': "Suspicion of fraud (ETHOCA)", + '30381001': "A technical problem occurred, please contact helpdesk", + '30401001': "Invalid function.", + '30411001': "Lost card.", + '30431001': "Stolen card, pick up", + '30511001': "Insufficient funds.", + '30521001': "No Authorization. Contact the issuer of your card.", + '30541001': "Card expired.", + '30551001': "Invalid PIN.", + '30561001': "Card not in authorizer's database.", + '30571001': "Transaction not permitted on card.", + '30581001': "Transaction not allowed on this terminal", + '30591001': "Suspicion of fraud.", + '30601001': "The merchant must contact the acquirer.", + '30611001': "Amount exceeds card ceiling.", + '30621001': "Restricted card.", + '30631001': "Security policy not respected.", + '30641001': "Amount changed from ref. trn.", + '30681001': "Tardy response.", + '30751001': "PIN entered incorrectly too often", + '30761001': "Card holder already contesting.", + '30771001': "PIN entry required.", + '30811001': "Message flow error.", + '30821001': "Authorization center unavailable", + '30831001': "Authorization center unavailable", + '30901001': "Temporary system shutdown.", + '30911001': "Acquirer unavailable.", + '30921001': "Invalid card type for acquirer.", + '30941001': "Duplicate transaction", + '30961001': "Processing temporarily not possible", + '30971001': "A technical problem occurred, please contact helpdesk", + '30981001': "A technical problem occurred, please contact helpdesk", + '31011001': "Unknown acceptance code", + '31021001': "Invalid currency", + '31031001': "Acceptance code missing", + '31041001': "Inactive card", + '31051001': "Merchant not active", + '31061001': "Invalid expiration date", + '31071001': "Interrupted host communication", + '31081001': "Card refused", + '31091001': "Invalid password", + '31101001': "Plafond transaction (majoré du bonus) dépassé", + '31111001': "Plafond mensuel (majoré du bonus) dépassé", + '31121001': "Plafond centre de facturation dépassé", + '31131001': "Plafond entreprise dépassé", + '31141001': "Code MCC du fournisseur non autorisé pour la carte", + '31151001': "Numéro SIRET du fournisseur non autorisé pour la carte", + '31161001': "This is not a valid online banking account", + '32001004': "A technical problem occurred, please try again.", + '34011001': "Bezahlung mit RatePAY nicht möglich.", + '39991001': "A technical problem occurred, please contact the helpdesk of your acquirer", + '40001001': "A technical problem occurred, please try again.", + '40001002': "A technical problem occurred, please try again.", + '40001003': "A technical problem occurred, please try again.", + '40001004': "A technical problem occurred, please try again.", + '40001005': "A technical problem occurred, please try again.", + '40001006': "A technical problem occurred, please try again.", + '40001007': "A technical problem occurred, please try again.", + '40001008': "A technical problem occurred, please try again.", + '40001009': "A technical problem occurred, please try again.", + '40001010': "A technical problem occurred, please try again.", + '40001011': "A technical problem occurred, please contact helpdesk", + '40001012': "Your merchant's acquirer is temporarily unavailable, please try later or choose another payment method.", + '40001013': "A technical problem occurred, please contact helpdesk", + '40001016': "A technical problem occurred, please contact helpdesk", + '40001018': "A technical problem occurred, please try again.", + '40001019': "Sorry, an error occurred during processing. Please retry the operation (use back button of the browser). If problem persists, contact your merchant's helpdesk.", + '40001020': "Sorry, an error occurred during processing. Please retry the operation (use back button of the browser). If problem persists, contact your merchant's helpdesk.", + '40001050': "A technical problem occurred, please contact helpdesk", + '40001133': "Authentication failed, the signature of your bank access control server is incorrect", + '40001134': "Authentication failed, please retry or cancel.", + '40001135': "Authentication temporary unavailable, please retry or cancel.", + '40001136': "Technical problem with your browser, please retry or cancel", + '40001137': "Your bank access control server is temporary unavailable, please retry or cancel", + '40001998': "Temporary technical problem. Please retry a little bit later.", + '50001001': "Unknown card type", + '50001002': "Card number format check failed for given card number.", + '50001003': "Merchant data error", + '50001004': "Merchant identification missing", + '50001005': "Expiry date error", + '50001006': "Amount is not a number", + '50001007': "A technical problem occurred, please contact helpdesk", + '50001008': "A technical problem occurred, please contact helpdesk", + '50001009': "A technical problem occurred, please contact helpdesk", + '50001010': "A technical problem occurred, please contact helpdesk", + '50001011': "Brand not supported for that merchant", + '50001012': "A technical problem occurred, please contact helpdesk", + '50001013': "A technical problem occurred, please contact helpdesk", + '50001014': "A technical problem occurred, please contact helpdesk", + '50001015': "Invalid currency code", + '50001016': "A technical problem occurred, please contact helpdesk", + '50001017': "A technical problem occurred, please contact helpdesk", + '50001018': "A technical problem occurred, please contact helpdesk", + '50001019': "A technical problem occurred, please contact helpdesk", + '50001020': "A technical problem occurred, please contact helpdesk", + '50001021': "A technical problem occurred, please contact helpdesk", + '50001022': "A technical problem occurred, please contact helpdesk", + '50001023': "A technical problem occurred, please contact helpdesk", + '50001024': "A technical problem occurred, please contact helpdesk", + '50001025': "A technical problem occurred, please contact helpdesk", + '50001026': "A technical problem occurred, please contact helpdesk", + '50001027': "A technical problem occurred, please contact helpdesk", + '50001028': "A technical problem occurred, please contact helpdesk", + '50001029': "A technical problem occurred, please contact helpdesk", + '50001030': "A technical problem occurred, please contact helpdesk", + '50001031': "A technical problem occurred, please contact helpdesk", + '50001032': "A technical problem occurred, please contact helpdesk", + '50001033': "A technical problem occurred, please contact helpdesk", + '50001034': "A technical problem occurred, please contact helpdesk", + '50001035': "A technical problem occurred, please contact helpdesk", + '50001036': "Card length does not correspond to an acceptable value for the brand", + '50001037': "Purchasing card number for a regular merchant", + '50001038': "Non Purchasing card for a Purchasing card merchant", + '50001039': "Details sent for a non-Purchasing card merchant, please contact helpdesk", + '50001040': "Details not sent for a Purchasing card transaction, please contact helpdesk", + '50001041': "Payment detail validation failed", + '50001042': "Given transactions amounts (tax,discount,shipping,net,etc…) do not compute correctly together", + '50001043': "A technical problem occurred, please contact helpdesk", + '50001044': "No acquirer configured for this operation", + '50001045': "No UID configured for this operation", + '50001046': "Operation not allowed for the merchant", + '50001047': "A technical problem occurred, please contact helpdesk", + '50001048': "A technical problem occurred, please contact helpdesk", + '50001049': "A technical problem occurred, please contact helpdesk", + '50001050': "A technical problem occurred, please contact helpdesk", + '50001051': "A technical problem occurred, please contact helpdesk", + '50001052': "A technical problem occurred, please contact helpdesk", + '50001053': "A technical problem occurred, please contact helpdesk", + '50001054': "Card number incorrect or incompatible", + '50001055': "A technical problem occurred, please contact helpdesk", + '50001056': "A technical problem occurred, please contact helpdesk", + '50001057': "A technical problem occurred, please contact helpdesk", + '50001058': "A technical problem occurred, please contact helpdesk", + '50001059': "A technical problem occurred, please contact helpdesk", + '50001060': "A technical problem occurred, please contact helpdesk", + '50001061': "A technical problem occurred, please contact helpdesk", + '50001062': "A technical problem occurred, please contact helpdesk", + '50001063': "Card Issue Number does not correspond to range or not present", + '50001064': "Start Date not valid or not present", + '50001066': "Format of CVC code invalid", + '50001067': "The merchant is not enrolled for 3D-Secure", + '50001068': "The card number or account number (PAN) is invalid", + '50001069': "Invalid check for CardID and Brand", + '50001070': "The ECI value given is either not supported, or in conflict with other data in the transaction", + '50001071': "Incomplete TRN demat", + '50001072': "Incomplete PAY demat", + '50001073': "No demat APP", + '50001074': "Authorisation too old", + '50001075': "VERRes was an error message", + '50001076': "DCP amount greater than authorisation amount", + '50001077': "Details negative amount", + '50001078': "Details negative quantity", + '50001079': "Could not decode/decompress received PARes (3D-Secure)", + '50001080': "Received PARes was an erereor message from ACS (3D-Secure)", + '50001081': "Received PARes format was invalid according to the 3DS specifications (3D-Secure)", + '50001082': "PAReq/PARes reconciliation failure (3D-Secure)", + '50001084': "Maximum amount reached", + '50001087': "The transaction type requires authentication, please check with your bank.", + '50001090': "CVC missing at input, but CVC check asked", + '50001091': "ZIP missing at input, but ZIP check asked", + '50001092': "Address missing at input, but Address check asked", + '50001095': "Invalid date of birth", + '50001096': "Invalid commodity code", + '50001097': "The requested currency and brand are incompatible.", + '50001111': "Data validation error", + '50001113': "This order has already been processed", + '50001114': "Error pre-payment check page access", + '50001115': "Request not received in secure mode", + '50001116': "Unknown IP address origin", + '50001117': "NO IP address origin", + '50001118': "Pspid not found or not correct", + '50001119': "Password incorrect or disabled due to numbers of errors", + '50001120': "Invalid currency", + '50001121': "Invalid number of decimals for the currency", + '50001122': "Currency not accepted by the merchant", + '50001123': "Card type not active", + '50001124': "Number of lines don't match with number of payments", + '50001125': "Format validation error", + '50001126': "Overflow in data capture requests for the original order", + '50001127': "The original order is not in a correct status", + '50001128': "missing authorization code for unauthorized order", + '50001129': "Overflow in refunds requests", + '50001130': "Error access to original order", + '50001131': "Error access to original history item", + '50001132': "The Selected Catalog is empty", + '50001133': "Duplicate request", + '50001134': "Authentication failed, please retry or cancel.", + '50001135': "Authentication temporary unavailable, please retry or cancel.", + '50001136': "Technical problem with your browser, please retry or cancel", + '50001137': "Your bank access control server is temporary unavailable, please retry or cancel", + '50001150': "Fraud Detection, Technical error (IP not valid)", + '50001151': "Fraud detection : technical error (IPCTY unknown or error)", + '50001152': "Fraud detection : technical error (CCCTY unknown or error)", + '50001153': "Overflow in redo-authorisation requests", + '50001170': "Dynamic BIN check failed", + '50001171': "Dynamic country check failed", + '50001172': "Error in Amadeus signature", + '50001174': "Card Holder Name is too long", + '50001175': "Name contains invalid characters", + '50001176': "Card number is too long", + '50001177': "Card number contains non-numeric info", + '50001178': "Card Number Empty", + '50001179': "CVC too long", + '50001180': "CVC contains non-numeric info", + '50001181': "Expiration date contains non-numeric info", + '50001182': "Invalid expiration month", + '50001183': "Expiration date must be in the future", + '50001184': "SHA Mismatch", + '50001205': "Missing mandatory fields for billing address.", + '50001206': "Missing mandatory field date of birth.", + '50001207': "Missing required shopping basket details.", + '50001208': "Missing social security number", + '50001209': "Invalid country code", + '50001210': "Missing yearly salary", + '50001211': "Missing gender", + '50001212': "Missing email", + '50001213': "Missing IP address", + '50001214': "Missing part payment campaign ID", + '50001215': "Missing invoice number", + '50001216': "The alias must be different than the card number", + '60000001': "account number unknown", + '60000003': "not credited dd-mm-yy", + '60000005': "name/number do not correspond", + '60000007': "account number blocked", + '60000008': "specific direct debit block", + '60000009': "account number WKA", + '60000010': "administrative reason", + '60000011': "account number expired", + '60000012': "no direct debit authorisation given", + '60000013': "debit not approved", + '60000014': "double payment", + '60000018': "name/address/city not entered", + '60001001': "no original direct debit for revocation", + '60001002': "payer’s account number format error", + '60001004': "payer’s account at different bank", + '60001005': "payee’s account at different bank", + '60001006': "payee’s account number format error", + '60001007': "payer’s account number blocked", + '60001008': "payer’s account number expired", + '60001009': "payee’s account number expired", + '60001010': "direct debit not possible", + '60001011': "creditor payment not possible", + '60001012': "payer’s account number unknown WKA-number", + '60001013': "payee’s account number unknown WKA-number", + '60001014': "impermissible WKA transaction", + '60001015': "period for revocation expired", + '60001017': "reason for revocation not correct", + '60001018': "original run number not numeric", + '60001019': "payment ID incorrect", + '60001020': "amount not numeric", + '60001021': "amount zero not permitted", + '60001022': "negative amount not permitted", + '60001023': "payer and payee giro account number", + '60001025': "processing code (verwerkingscode) incorrect", + '60001028': "revocation not permitted", + '60001029': "guaranteed direct debit on giro account number", + '60001030': "NBC transaction type incorrect", + '60001031': "description too large", + '60001032': "book account number not issued", + '60001034': "book account number incorrect", + '60001035': "payer’s account number not numeric", + '60001036': "payer’s account number not eleven-proof", + '60001037': "payer’s account number not issued", + '60001039': "payer’s account number of DNB/BGC/BLA", + '60001040': "payee’s account number not numeric", + '60001041': "payee’s account number not eleven-proof", + '60001042': "payee’s account number not issued", + '60001044': "payee’s account number unknown", + '60001050': "payee’s name missing", + '60001051': "indicate payee’s bank account number instead of 3102", + '60001052': "no direct debit contract", + '60001053': "amount beyond bounds", + '60001054': "selective direct debit block", + '60001055': "original run number unknown", + '60001057': "payer’s name missing", + '60001058': "payee’s account number missing", + '60001059': "restore not permitted", + '60001060': "bank’s reference (navraaggegeven) missing", + '60001061': "BEC/GBK number incorrect", + '60001062': "BEC/GBK code incorrect", + '60001087': "book account number not numeric", + '60001090': "cancelled on request", + '60001091': "cancellation order executed", + '60001092': "cancelled instead of bended", + '60001093': "book account number is a shortened account number", + '60001094': "instructing party account number not identical with payer", + '60001095': "payee unknown GBK acceptor", + '60001097': "instructing party account number not identical with payee", + '60001099': "clearing not permitted", + '60001101': "payer’s account number not spaces", + '60001102': "PAN length not numeric", + '60001103': "PAN length outside limits", + '60001104': "track number not numeric", + '60001105': "track number not valid", + '60001106': "PAN sequence number not numeric", + '60001107': "domestic PAN not numeric", + '60001108': "domestic PAN not eleven-proof", + '60001109': "domestic PAN not issued", + '60001110': "foreign PAN not numeric", + '60001111': "card valid date not numeric", + '60001112': "book period number (boekperiodenr) not numeric", + '60001113': "transaction number not numeric", + '60001114': "transaction time not numeric", + '60001115': "transaction no valid time", + '60001116': "transaction date not numeric", + '60001117': "transaction no valid date", + '60001118': "STAN not numeric", + '60001119': "instructing party’s name missing", + '60001120': "foreign amount (bedrag-vv) not numeric", + '60001122': "rate (verrekenkoers) not numeric", + '60001125': "number of decimals (aantaldecimalen) incorrect", + '60001126': "tariff (tarifering) not B/O/S", + '60001127': "domestic costs (kostenbinnenland) not numeric", + '60001128': "domestic costs (kostenbinnenland) not higher than zero", + '60001129': "foreign costs (kostenbuitenland) not numeric", + '60001130': "foreign costs (kostenbuitenland) not higher than zero", + '60001131': "domestic costs (kostenbinnenland) not zero", + '60001132': "foreign costs (kostenbuitenland) not zero", + '60001134': "Euro record not fully filled in", + '60001135': "Client currency incorrect", + '60001136': "Amount NLG not numeric", + '60001137': "Amount NLG not higher than zero", + '60001138': "Amount NLG not equal to Amount", + '60001139': "Amount NLG incorrectly converted", + '60001140': "Amount EUR not numeric", + '60001141': "Amount EUR not greater than zero", + '60001142': "Amount EUR not equal to Amount", + '60001143': "Amount EUR incorrectly converted", + '60001144': "Client currency not NLG", + '60001145': "rate euro-vv (Koerseuro-vv) not numeric", + '60001146': "comma rate euro-vv (Kommakoerseuro-vv) incorrect", + '60001147': "acceptgiro distributor not valid", + '60001148': "Original run number and/or BRN are missing", + '60001149': "Amount/Account number/ BRN different", + '60001150': "Direct debit already revoked/restored", + '60001151': "Direct debit already reversed/revoked/restored", + '60001153': "Payer’s account number not known", +} + +DATA_VALIDATION_ERROR = '50001111' + + +def retryable(error): + return error in [ + '0020001001', '0020001002', '0020001003', '0020001004', '0020001005', + '0020001006', '0020001007', '0020001008', '0020001009', '0020001010', + '30001010', '30001011', '30001015', + '30001057', '30001058', + '30001998', '30001999', + #'30611001', # amount exceeds card limit + '30961001', + '40001001', '40001002', '40001003', '40001004', '40001005', + '40001006', '40001007', '40001008', '40001009', '40001010', + '40001012', + '40001018', '40001019', '40001020', + '40001134', '40001135', '40001136', '40001137', + #'50001174', # cardholder name too long + ] diff --git a/addons/payment_acquirer_ogone/data/ogone.xml b/addons/payment_acquirer_ogone/data/ogone.xml new file mode 100644 index 00000000000..2288ccc0b6e --- /dev/null +++ b/addons/payment_acquirer_ogone/data/ogone.xml @@ -0,0 +1,17 @@ + + + + + + ogone + + test + pinky + OOAPI + R!ci/6Nu8a + tINY4Yv14789gUix1130 + tINYj885Tfvd4P471464 + + + + diff --git a/addons/payment_acquirer_ogone/models/__init__.py b/addons/payment_acquirer_ogone/models/__init__.py new file mode 100644 index 00000000000..61f4cbeacf3 --- /dev/null +++ b/addons/payment_acquirer_ogone/models/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import ogone diff --git a/addons/payment_acquirer_ogone/models/ogone.py b/addons/payment_acquirer_ogone/models/ogone.py new file mode 100644 index 00000000000..af7535ade8e --- /dev/null +++ b/addons/payment_acquirer_ogone/models/ogone.py @@ -0,0 +1,397 @@ +# -*- coding: utf-'8' "-*-" +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from hashlib import sha1 +import logging +from lxml import etree, objectify +from pprint import pformat +# import requests +import time +from urllib import urlencode +import urllib2 +# import urlparse + +from openerp.addons.payment_acquirer.data import ogone +from openerp.addons.payment_acquirer.models.payment_acquirer import ValidationError +from openerp.addons.payment_acquirer_ogone.controllers.main import OgoneController +from openerp.osv import osv, fields +from openerp.tools import float_round + +_logger = logging.getLogger(__name__) + + +class PaymentAcquirerOgone(osv.Model): + _inherit = 'payment.acquirer' + + def _get_ogone_urls(self, cr, uid, ids, name, args, context=None): + """ Ogone URLS: + + - standard order: POST address for form-based + + @TDETODO: complete me + """ + res = {} + for acquirer in self.browse(cr, uid, ids, context=context): + qualif = acquirer.env + res[acquirer.id] = { + 'ogone_standard_order_url': 'https://secure.ogone.com/ncol/%s/orderstandard.asp' % qualif, + 'ogone_direct_order_url': 'https://secure.ogone.com/ncol/%s/orderdirect.asp' % qualif, + 'ogone_direct_query_url': 'https://secure.ogone.com/ncol/%s/querydirect.asp' % qualif, + 'ogone_afu_agree_url': 'https://secure.ogone.com/ncol/%s/AFU_agree.asp' % qualif, + } + return res + + _columns = { + 'ogone_pspid': fields.char( + 'PSPID', required_if_provider='ogone'), + 'ogone_userid': fields.char( + 'API User id', required_if_provider='ogone'), + 'ogone_password': fields.char( + 'Password', required_if_provider='ogone'), + 'ogone_shakey_in': fields.char( + 'SHA Key IN', size=32, required_if_provider='ogone'), + 'ogone_shakey_out': fields.char( + 'SHA Key OUT', size=32, required_if_provider='ogone'), + # store ogone contact URLs -> not necessary IMHO + 'ogone_standard_order_url': fields.function( + _get_ogone_urls, type='char', multi='_get_ogone_urls', + string='Stanrd Order URL (form)'), + 'ogone_direct_order_url': fields.function( + _get_ogone_urls, type='char', multi='_get_ogone_urls', + string='Direct Order URL (2)'), + 'ogone_direct_query_url': fields.function( + _get_ogone_urls, type='char', multi='_get_ogone_urls', + string='Direct Query URL'), + 'ogone_afu_agree_url': fields.function( + _get_ogone_urls, type='char', multi='_get_ogone_urls', + string='AFU Agree URL'), + } + + def _ogone_generate_shasign(self, acquirer, inout, values): + """ Generate the shasign for incoming or outgoing communications. + + :param browse acquirer: the payment.acquirer browse record. It should + have a shakey in shaky out + :param string inout: 'in' (openerp contacting ogone) or 'out' (ogone + contacting openerp). In this last case only some + fields should be contained (see e-Commerce basic) + :param dict values: transaction values + + :return string: shasign + """ + assert inout in ('in', 'out') + assert acquirer.name == 'ogone' + key = getattr(acquirer, 'ogone_shakey_' + inout) + + def filter_key(key): + if inout == 'in': + return True + else: + keys = "ORDERID CURRENCY AMOUNT PM ACCEPTANCE STATUS CARDNO ALIAS ED CN TRXDATE PAYID NCERROR BRAND ECI IP COMPLUS".split() + return key.upper() in keys + + items = sorted((k.upper(), v) for k, v in values.items()) + sign = ''.join('%s=%s%s' % (k, v, key) for k, v in items if v and filter_key(k)) + shasign = sha1(sign).hexdigest() + return shasign + + def ogone_form_generate_values(self, cr, uid, id, reference, amount, currency, partner_id=False, partner_values=None, tx_custom_values=None, context=None): + if partner_values is None: + partner_values = {} + base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url') + acquirer = self.browse(cr, uid, id, context=context) + partner = None + if partner_id: + partner = self.pool['res.partner'].browse(cr, uid, partner_id, context=context) + tx_values = { + 'PSPID': acquirer.ogone_pspid, + 'ORDERID': reference, + 'AMOUNT': '%d' % int(float_round(amount, 2) * 100), + 'CURRENCY': currency and currency.name or 'EUR', + 'LANGUAGE': partner and partner.lang or partner_values.get('lang', ''), + 'CN': partner and partner.name or partner_values.get('name', ''), + 'EMAIL': partner and partner.email or partner_values.get('email', ''), + 'OWNERZIP': partner and partner.zip or partner_values.get('zip', ''), + 'OWNERADDRESS': partner and ' '.join((partner.street, partner.street2)).strip() or ' '.join((partner_values.get('street', ''), partner_values.get('street2', ''))).strip(), + 'OWNERTOWN': partner and partner.city or partner_values.get('city', ''), + 'OWNERCTY': partner and partner.country_id and partner.country_id.name or partner_values.get('country_name', ''), + 'OWNERTELNO': partner and partner.phone or partner_values.get('phone', ''), + 'ACCEPTURL': '%s/%s' % (base_url, OgoneController._accept_url), + 'DECLINEURL': '%s/%s' % (base_url, OgoneController._decline_url), + 'EXCEPTIONURL': '%s/%s' % (base_url, OgoneController._exception_url), + 'CANCELURL': '%s/%s' % (base_url, OgoneController._cancel_url), + } + if tx_custom_values: + tx_values.update(tx_custom_values) + shasign = self._ogone_generate_shasign(acquirer, 'in', tx_values) + tx_values['SHASIGN'] = shasign + return tx_values + + +class PaymentTxOgone(osv.Model): + _inherit = 'payment.transaction' + + _columns = { + 'ogone_3ds': fields.dummy('3ds Activated'), + 'ogone_3ds_html': fields.html('3DS HTML'), + 'ogone_feedback_model': fields.char(), + 'ogone_feedback_eval': fields.char(), + 'ogone_complus': fields.char('Complus'), + } + + # -------------------------------------------------- + # FORM RELATED METHODS + # -------------------------------------------------- + + def _ogone_form_get_tx_from_shasign_out(self, cr, uid, data, context=None): + reference, pay_id, shasign = data.get('orderID'), data.get('PAYID'), data.get('SHASIGN') + if not reference or not pay_id or not shasign: + error_msg = 'Ogone: received data with missing reference (%s) or pay_id (%s) or shashign (%s)' % (reference, pay_id, shasign) + _logger.error(error_msg) + raise ValidationError(error_msg) + + # find tx -> @TDENOTE use paytid ? + tx_ids = self.pool['payment.transaction'].search(cr, uid, [('reference', '=', reference)], context=context) + if not tx_ids or len(tx_ids) > 1: + error_msg = 'Ogone: received data for reference' % (reference) + if not tx_ids: + error_msg += '; no order found' + else: + error_msg += '; multiple order found' + _logger.error(error_msg) + raise ValidationError(error_msg) + tx = self.pool['payment.transaction'].browse(cr, uid, tx_ids[0], context=context) + + # verify shasign + shasign_check = self.pool['payment.acquirer']._generate_ogone_shasign(tx.acquirer_id, 'out', data) + if shasign_check.upper() != shasign.upper(): + error_msg = 'Ogone: invalid shasign, received %s, computed %s, for data %s' % (shasign, shasign_check, data) + _logger.error(error_msg) + raise ValidationError(error_msg) + + return tx + + def ogone_form_generate_values(self, cr, uid, id, tx_custom_values=None, context=None): + tx = self.browse(cr, uid, id, context=context) + + tx_data = { + 'LANGUAGE': tx.partner_lang, + 'CN': tx.partner_name, + 'EMAIL': tx.partner_email, + 'OWNERZIP': tx.partner_zip, + 'OWNERADDRESS': tx.partner_address, + 'OWNERTOWN': tx.partner_city, + 'OWNERCTY': tx.partner_country_id and tx.partner_country_id.name or '', + 'OWNERTELNO': tx.partner_phone, + } + if tx_custom_values: + tx_data.update(tx_custom_values) + return self.pool['payment.acquirer'].ogone_form_generate_values( + cr, uid, tx.acquirer_id.id, + tx.reference, tx.amount, tx.currency_id, + tx_custom_values=tx_data, + context=context + ) + + def ogone_form_feedback(self, cr, uid, data, context=None): + print '-- ogone: ogone_form_feedback' + tx = self._ogone_get_tx_from_shasign_out(cr, uid, data, context) + if not tx: + raise ValidationError('Ogone: feedback: tx not found') + + status = int(data.get('STATUS', '0')) + print '\togone: statuts %s' % status + if status in [5, 9]: + tx.write({'state': 'done'}) + return True + else: + error = 'Ogone: feedback error: %(error_str)s\n\n%(error_code)s: %(error_msg)s' % { + 'error_str': data.get('NCERROR'), + 'error_code': data.get('NCERRORPLUS'), + 'error_msg': ogone.OGONE_ERROR_MAP.get(data.get('NCERRORPLUS')), + } + _logger.info(error) + tx.write({'state': 'error', 'state_message': error}) + return False + + # -------------------------------------------------- + # S2S RELATED METHODS + # -------------------------------------------------- + + def ogone_s2s_create_alias(self, cr, uid, id, values, context=None): + """ Purpose: create an alias via batch """ + tx = self.browse(cr, uid, id, context=context) + assert tx.type == 'server2server', 'Calling s2s dedicated method for a %s acquirer' % tx.type + alias = 'OPENERP-%d-%d' % (tx.partner_id.id, tx.id) + + expiry_date = '%s%s' % (values['expiry_date_mm'], values['expiry_date_yy'][2:]) + line = 'ADDALIAS;%(alias)s;%(holder_name)s;%(number)s;%(expiry_date)s;%(brand)s;%(pspid)s' + line = line % dict(values, alias=alias, expiry_date=expiry_date, pspid=tx.acquirer_id.ogone_pspid) + + tx_data = { + 'FILE_REFERENCE': 'OPENERP-NEW-ALIAS-%s' % time.time(), # something unique, + 'TRANSACTION_CODE': 'ATR', + 'OPERATION': 'SAL', + 'NB_PAYMENTS': 1, # even if we do not actually have any payment, ogone want it to not be 0 + 'FILE': line, + 'REPLY_TYPE': 'XML', + 'PSPID': tx.acquirer_id.ogone_pspid, + 'USERID': tx.acquirer_id.ogone_userid, + 'PSWD': tx.acquirer_id.ogone_password, + 'PROCESS_MODE': 'CHECKANDPROCESS', + } + + request = urllib2.Request(tx.acquirer_id.ogone_afu_agree_url, urlencode(tx_data)) + result = urllib2.urlopen(request).read() + + try: + tree = objectify.fromstring(result) + except etree.XMLSyntaxError: + _logger.exception('Invalid xml response from ogone') + return None + + error_code = error_str = None + if hasattr(tree, 'PARAMS_ERROR'): + error_code = tree.NCERROR.text + error_str = 'PARAMS ERROR: %s' % (tree.PARAMS_ERROR.text or '',) + else: + node = tree.FORMAT_CHECK + error_node = getattr(node, 'FORMAT_CHECK_ERROR', None) + if error_node is not None: + error_code = error_node.NCERROR.text + error_str = 'CHECK ERROR: %s' % (error_node.ERROR.text or '',) + + if error_code: + error_msg = ogone.OGONE_ERROR_MAP.get(error_code) + error = '%s\n\n%s: %s' % (error_str, error_code, error_msg) + _logger.error(error) + raise Exception(error) # TODO specific exception + + tx.write({'partner_reference': alias}) + return True + + def ogone_s2s_generate_values(self, cr, uid, id, custom_values, context=None): + tx = self.browse(cr, uid, id, context=context) + tx_data = { + 'PSPID': tx.acquirer_id.ogone_pspid, + 'USERID': tx.acquirer_id.ogone_userid, + 'PSWD': tx.acquirer_id.ogone_password, + 'OrderID': tx.reference, + 'amount': '%d' % int(float_round(tx.amount, 2) * 100), # tde check amount or str * 100 ? + 'CURRENCY': tx.currency_id.name, + 'LANGUAGE': tx.partner_lang, + 'OPERATION': 'SAL', + 'ECI': 2, # Recurring (from MOTO) + 'ALIAS': tx.partner_reference, + 'RTIMEOUT': 30, + } + if custom_values.get('ogone_cvc'): + tx_data['CVC'] = custom_values.get('ogone_cvc') + if custom_values.pop('ogone_3ds', None): + tx_data.update({ + 'FLAG3D': 'Y', # YEAH!! + }) + if custom_values.get('ogone_complus'): + tx_data['COMPLUS'] = custom_values.get('ogone_complus') + if custom_values.get('ogone_accept_url'): + pass + + shasign = self.pool['payment.acquirer']._ogone_generate_shasign(tx.acquirer_id, 'in', tx_data) + tx_data['SHASIGN'] = shasign + return tx_data + + def ogone_s2s_execute(self, cr, uid, id, values, context=None): + tx = self.browse(cr, uid, id, context=context) + + tx_data = self.ogone_s2s_generate_values(cr, uid, id, values, context=context) + _logger.info('Generated Ogone s2s data %s', pformat(tx_data)) # debug + + request = urllib2.Request(tx.acquirer_id.ogone_direct_order_url, urlencode(tx_data)) + result = urllib2.urlopen(request).read() + _logger.info('Contacted Ogone direct order; result %s', result) # debug + + tree = objectify.fromstring(result) + payid = tree.get('PAYID') + print 'payid', payid + + query_direct_data = dict( + PSPID=tx.acquirer_id.ogone_pspid, + USERID=tx.acquirer_id.ogone_userid, + PSWD=tx.acquirer_id.ogone_password, + ID=payid, + ) + query_direct_url = 'https://secure.ogone.com/ncol/%s/querydirect.asp' % (tx.acquirer_id.env,) + + tries = 2 + tx_done = False + tx_status = False + while not tx_done or tries > 0: + try: + tree = objectify.fromstring(result) + except etree.XMLSyntaxError: + # invalid response from ogone + _logger.exception('Invalid xml response from ogone') + raise + + # see https://secure.ogone.com/ncol/paymentinfos1.asp + VALID_TX = [5, 9] + WAIT_TX = [41, 50, 51, 52, 55, 56, 91, 92, 99] + PENDING_TX = [46] # 3DS HTML response + # other status are errors... + + status = tree.get('STATUS') + if status == '': + status = None + else: + status = int(status) + + if status in VALID_TX: + tx_status = True + tx_done = True + + elif status in PENDING_TX: + html = str(tree.HTML_ANSWER) + tx_data.update(ogone_3ds_html=html.decode('base64')) + tx_status = False + tx_done = True + + elif status in WAIT_TX: + time.sleep(1500) + + request = urllib2.Request(query_direct_url, urlencode(query_direct_data)) + result = urllib2.urlopen(request).read() + _logger.debug('Contacted Ogone query direct; result %s', result) + + else: + error_code = tree.get('NCERROR') + if not ogone.retryable(error_code): + error_str = tree.get('NCERRORPLUS') + error_msg = ogone.OGONE_ERROR_MAP.get(error_code) + error = 'ERROR: %s\n\n%s: %s' % (error_str, error_code, error_msg) + _logger.info(error) + raise Exception(error) + + tries = tries - 1 + + if not tx_done and tries == 0: + raise Exception('Cannot get transaction status...') + + return (tx_status, orderid, payid) diff --git a/addons/payment_acquirer_ogone/tests/__init__.py b/addons/payment_acquirer_ogone/tests/__init__.py new file mode 100644 index 00000000000..2381425068e --- /dev/null +++ b/addons/payment_acquirer_ogone/tests/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2012-TODAY OpenERP S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from . import test_ogone + +checks = [ + test_ogone, +] + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/payment_acquirer_ogone/tests/test_ogone.py b/addons/payment_acquirer_ogone/tests/test_ogone.py new file mode 100644 index 00000000000..cb34ed1ddda --- /dev/null +++ b/addons/payment_acquirer_ogone/tests/test_ogone.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2012-TODAY OpenERP S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.addons.payment_acquirer.tests.common import PaymentAcquirerCommon +from openerp.addons.payment_acquirer_ogone.controllers.main import OgoneController +# from openerp.osv.orm import except_orm + +from lxml import objectify +# import requests +# import urlparse + + +class BasicPayment(PaymentAcquirerCommon): + + def test_20_ogone_form(self): + cr, uid = self.cr, self.uid + context = {} + base_url = self.registry('ir.config_parameter').get_param(cr, uid, 'web.base.url') + # ogone_url = self.payment_acquirer._get_ogone_urls(cr, uid, [ogone_id], None, None)[ogone_id]['ogone_standard_order_url'] + + model, ogone_view_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'payment_acquirer_ogone', 'ogone_acquirer_button') + + # create a new ogone account + ogone_id = self.payment_acquirer.create( + cr, uid, { + 'name': 'ogone', + 'env': 'test', + 'view_template_id': ogone_view_id, + 'ogone_pspid': 'pinky', + 'ogone_userid': 'OOAPI', + 'ogone_password': 'R!ci/6Nu8a', + 'ogone_shakey_in': 'tINY4Yv14789gUix1130', + 'ogone_shakey_out': 'tINYj885Tfvd4P471464', + }, context=context + ) + # verify acquirer data + ogone = self.payment_acquirer.browse(cr, uid, ogone_id, context) + self.assertEqual(ogone.env, 'test', 'test without test env') + + form_values = { + 'PSPID': 'pinky', + 'ORDERID': 'test_ref0', + 'AMOUNT': '1', + 'CURRENCY': 'EUR', + 'LANGUAGE': 'en_US', + 'CN': 'Norbert Buyer', + 'EMAIL': 'norbert.buyer@example.com', + 'OWNERZIP': '1000', + 'OWNERADDRESS': 'Huge Street 2/543', + 'OWNERCTY': 'Belgium', + 'OWNERTOWN': 'Sin City', + 'OWNERTELNO': '0032 12 34 56 78', + 'SHASIGN': 'ea74bb42d4f25746279cdd44a737aaddc71e7f9f', + 'ACCEPTURL': '%s/%s' % (base_url, OgoneController._accept_url), + 'DECLINEURL': '%s/%s' % (base_url, OgoneController._decline_url), + 'EXCEPTIONURL': '%s/%s' % (base_url, OgoneController._exception_url), + 'CANCELURL': '%s/%s' % (base_url, OgoneController._cancel_url), + } + + # render the button + res = self.payment_acquirer.render( + cr, uid, ogone_id, + 'test_ref0', 0.01, self.currency_euro, + partner_id=None, + partner_values=self.buyer_values, + context=context) + # check form result + tree = objectify.fromstring(res) + self.assertEqual(tree.get('action'), 'https://secure.ogone.com/ncol/test/orderstandard.asp', 'ogone: wrong form POST url') + for form_input in tree.input: + if form_input.get('name') in ['submit']: + continue + self.assertEqual( + form_input.get('value'), + form_values[form_input.get('name')], + 'ogone: wrong value for form: received %s instead of %s' % (form_input.get('value'), form_values[form_input.get('name')]) + ) + # resp = requests.post(tree.get('action'), data=form_values) + + # create a new draft tx + tx_id = self.payment_transaction.create( + cr, uid, { + 'amount': 0.01, + 'acquirer_id': ogone_id, + 'currency_id': self.currency_euro_id, + 'reference': 'test_ref0', + 'partner_id': self.buyer_id, + }, context=context + ) + # render the button + res = self.payment_acquirer.render( + cr, uid, ogone_id, + 'test_ref0', 0.01, self.currency_euro, + tx_id=tx_id, + partner_id=None, + partner_values=self.buyer_values, + context=context) + # check form result + tree = objectify.fromstring(res) + self.assertEqual(tree.get('action'), 'https://secure.ogone.com/ncol/test/orderstandard.asp', 'ogone: wrong form POST url') + for form_input in tree.input: + if form_input.get('name') in ['submit']: + continue + self.assertEqual( + form_input.get('value'), + form_values[form_input.get('name')], + 'ogone: wrong value for form input %s: received %s instead of %s' % (form_input.get('name'), form_input.get('value'), form_values[form_input.get('name')]) + ) + + def test_21_ogone_s2s(self): + cr, uid = self.cr, self.uid + context = {} + + model, ogone_view_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'payment_acquirer_ogone', 'ogone_acquirer_button') + + # create a new ogone account + ogone_id = self.payment_acquirer.create( + cr, uid, { + 'name': 'ogone', + 'env': 'test', + 'view_template_id': ogone_view_id, + 'ogone_pspid': 'pinky', + 'ogone_userid': 'OOAPI', + 'ogone_password': 'R!ci/6Nu8a', + 'ogone_shakey_in': 'tINY4Yv14789gUix1130', + 'ogone_shakey_out': 'tINYj885Tfvd4P471464', + }, context=context + ) + + # create a new draft tx + tx_id = self.payment_transaction.create( + cr, uid, { + 'amount': 0.01, + 'acquirer_id': ogone_id, + 'currency_id': self.currency_euro_id, + 'reference': 'test_ogone_0', + 'partner_id': self.buyer_id, + 'type': 'server2server', + }, context=context + ) + + res = self.payment_transaction.ogone_s2s_create_alias( + cr, uid, tx_id, { + 'expiry_date_mm': '01', + 'expiry_date_yy': '2015', + 'holder_name': 'Norbert Poilu', + 'number': '4000000000000002', + 'brand': 'VISA', + }, context=context) + print res + + res = self.payment_transaction.ogone_s2s_execute(cr, uid, tx_id, {}, context=context) + print res + + +# { +# 'orderID': u'reference', +# 'STATUS': u'9', +# 'CARDNO': u'XXXXXXXXXXXX0002', +# 'PAYID': u'24998692', +# 'CN': u'Norbert Poilu', +# 'NCERROR': u'0', +# 'TRXDATE': u'11/05/13', +# 'IP': u'85.201.233.72', +# 'BRAND': u'VISA', +# 'ACCEPTANCE': u'test123', +# 'currency': u'EUR', +# 'amount': u'1.95', +# 'SHASIGN': u'EFDC56879EF7DE72CCF4B397076B5C9A844CB0FA', +# 'ED': u'0314', +# 'PM': u'CreditCard' +# } diff --git a/addons/payment_acquirer_ogone/views/ogone.xml b/addons/payment_acquirer_ogone/views/ogone.xml new file mode 100644 index 00000000000..0945ebf22d8 --- /dev/null +++ b/addons/payment_acquirer_ogone/views/ogone.xml @@ -0,0 +1,44 @@ + + + + + + + + diff --git a/addons/payment_acquirer_paypal/__init__.py b/addons/payment_acquirer_paypal/__init__.py new file mode 100644 index 00000000000..dde6f25c728 --- /dev/null +++ b/addons/payment_acquirer_paypal/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import models +import controllers diff --git a/addons/payment_acquirer_paypal/__openerp__.py b/addons/payment_acquirer_paypal/__openerp__.py new file mode 100644 index 00000000000..d9ceb1eeb8f --- /dev/null +++ b/addons/payment_acquirer_paypal/__openerp__.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + 'name': 'Paypal Payment Acquirer', + 'category': 'Hidden', + 'summary': 'Paypal Payment Acquirer', + 'version': '0.1', + 'description': """Paypal Payment Acquirer""", + 'author': 'OpenERP SA', + 'depends': ['payment_acquirer'], + 'data': [ + 'views/paypal.xml', + 'data/paypal.xml', + ], + 'installable': True, +} diff --git a/addons/payment_acquirer_paypal/controllers/__init__.py b/addons/payment_acquirer_paypal/controllers/__init__.py new file mode 100644 index 00000000000..039d9715fab --- /dev/null +++ b/addons/payment_acquirer_paypal/controllers/__init__.py @@ -0,0 +1 @@ +import main \ No newline at end of file diff --git a/addons/payment_acquirer_paypal/controllers/main.py b/addons/payment_acquirer_paypal/controllers/main.py new file mode 100644 index 00000000000..7b9d654b30b --- /dev/null +++ b/addons/payment_acquirer_paypal/controllers/main.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.addons.web import http +from openerp.addons.web.http import request +# from openerp.addons.payment_acquirer.models.payment_acquirer import ValidationError +from openerp.addons.website.models import website + +import logging +import requests +from urllib import urlencode + +_logger = logging.getLogger(__name__) + + +class PaypalController(http.Controller): + _notify_url = '/payment/paypal/ipn/' + _return_url = '/payment/paypal/dpn/' + _cancel_url = '/payment/paypal/cancel/' + # _ipn_url2 = '/payment/paypal//ipn/' + + @website.route('/payment/paypal//ipn/', type='http', auth='admin') + def paypal_ipn(self, **post): + print 'Entering paypal_ipn with post', post + # step 1: return an empty HTTP 200 response -> will be done at the end by returning '' + + # step 2: POST the complete, unaltered message back to Paypal (preceded by cmd=_notify-validate), with same encoding + paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr" + post_url = '%s?cmd=_notify-validate&%s' % (paypal_url, urlencode(post)) + resp = requests.post(post_url) + print '\tReceived response', resp, resp.text + + # step 3: paypal send either VERIFIED or INVALID (single word) + if resp.text == 'VERIFIED': + # _logger.warning('') + cr, uid, context = request.cr, request.uid, request.context + # payment_transaction = request.registry['payment.transaction'] + # payment_transaction.validate() + elif resp.text == 'INVALID': + # _logger.warning('') + pass + else: + # _logger.warning('') -> something went wrong + pass + + return '' + + @website.route([ + '/payment/paypal/test/dpn', + ], type='http', auth="public") + def paypal_test_success(self, **post): + """ TODO + """ + cr, uid, context = request.cr, request.uid, request.context + print post + return '' diff --git a/addons/payment_acquirer_paypal/data/paypal.xml b/addons/payment_acquirer_paypal/data/paypal.xml new file mode 100644 index 00000000000..eab754a046c --- /dev/null +++ b/addons/payment_acquirer_paypal/data/paypal.xml @@ -0,0 +1,15 @@ + + + + + + paypal + + test + https://www.sandbox.paypal.com/cgi-bin/webscr + tde+paypal-facilitator@openerp.com + 'tde+paypal-facilitator_api1.openerp.com + + + + diff --git a/addons/payment_acquirer_paypal/models/__init__.py b/addons/payment_acquirer_paypal/models/__init__.py new file mode 100644 index 00000000000..d89c4a0fee3 --- /dev/null +++ b/addons/payment_acquirer_paypal/models/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import paypal diff --git a/addons/payment_acquirer_paypal/models/paypal.py b/addons/payment_acquirer_paypal/models/paypal.py new file mode 100644 index 00000000000..42113994e22 --- /dev/null +++ b/addons/payment_acquirer_paypal/models/paypal.py @@ -0,0 +1,241 @@ +# -*- coding: utf-'8' "-*-" +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.addons.payment_acquirer.models.payment_acquirer import ValidationError +from openerp.addons.payment_acquirer_paypal.controllers.main import PaypalController +from openerp.osv import osv, fields + +import logging +import requests +import urlparse + +_logger = logging.getLogger(__name__) + + +class AcquirerPaypal(osv.Model): + _inherit = 'payment.acquirer' + + _columns = { + 'paypal_email_id': fields.char('Email ID', required_if_provider='paypal'), + 'paypal_username': fields.char('Username', required_if_provider='paypal'), + 'paypal_password': fields.char('Password'), + 'paypal_signature': fields.char('Signature'), + 'paypal_tx_url': fields.char('Transaction URL', required_if_provider='paypal'), + 'paypal_use_dpn': fields.boolean('Use DPN'), + 'paypal_use_ipn': fields.boolean('Use IPN'), + } + + _defaults = { + 'paypal_use_dpn': True, + 'paypal_use_ipn': True, + 'paypal_tx_url': 'https://www.sandbox.paypal.com/cgi-bin/webscr', + } + + def paypal_form_generate_values(self, cr, uid, id, reference, amount, currency, partner_id=False, partner_values=None, tx_custom_values=None, context=None): + if partner_values is None: + partner_values = {} + base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url') + acquirer = self.browse(cr, uid, id, context=context) + partner = None + if partner_id: + partner = self.pool['res.partner'].browse(cr, uid, partner_id, context=context) + tx_values = { + 'cmd': '_xclick', + 'business': acquirer.paypal_email_id, + 'item_name': reference, + 'item_number': reference, + 'amount': amount, + 'currency_code': currency and currency.name or 'EUR', + 'address1': partner and ' '.join((partner.street, partner.street2)).strip() or ' '.join((partner_values.get('street', ''), partner_values.get('street2', ''))).strip(), + 'city': partner and partner.city or partner_values.get('city', ''), + 'country': partner and partner.country_id and partner.country_id.name or partner_values.get('country_name', ''), + 'email': partner and partner.email or partner_values.get('email', ''), + 'zip': partner and partner.zip or partner_values.get('zip', ''), + 'first_name': partner and partner.name or partner_values.get('name', '').split()[-1:], + 'last_name': partner and partner.name or partner_values.get('name', '').split()[:-1], + 'return': '%s/%s' % (base_url, PaypalController._return_url), + 'notify_url': '%s/%s' % (base_url, PaypalController._notify_url), + 'cancel_return': '%s/%s' % (base_url, PaypalController._cancel_url), + } + if tx_custom_values: + tx_values.update(tx_custom_values) + return tx_values + + +class TxPaypal(osv.Model): + _inherit = 'payment.transaction' + + _columns = { + 'paypal_txn_id': fields.char('Transaction ID'), + } + + # -------------------------------------------------- + # FORM RELATED METHODS + # -------------------------------------------------- + + def paypal_form_generate_values(self, cr, uid, id, tx_custom_values=None, context=None): + tx = self.browse(cr, uid, id, context=context) + + tx_data = { + 'item_name': tx.name, + 'first_name': tx.partner_name and tx.partner_name.split()[-1:], + 'last_name': tx.partner_name and tx.partner_name.split()[:-1], + 'email': tx.partner_email, + 'zip': tx.partner_zip, + 'address1': tx.partner_address, + 'city': tx.partner_city, + 'country': tx.partner_country_id and tx.partner_country_id.name or '', + } + if tx_custom_values: + tx_data.update(tx_custom_values) + return self.pool['payment.acquirer'].paypal_form_generate_values( + cr, uid, tx.acquirer_id.id, + tx.reference, tx.amount, tx.currency_id, + tx_custom_values=tx_data, + context=context + ) + + + + def validate_paypal_notification(self, cr, uid, url, context=None): + parsed_url = urlparse.urlparse(url) + query_parameters = parsed_url.query + parameters = urlparse.parse_qs(query_parameters) + + invalid_parameters = [] + + # check tx effectively exists + txn_id = parameters.get('txn_id')[0] + tx_ids = self.search(cr, uid, [('paypal_txn_id', '=', txn_id)], context=context) + if not tx_ids: + _logger.warning( + 'Received a notification from Paypal for a tx %s that does not exists in database.' % + txn_id + ) + return False + elif len(tx_ids) > 1: + _logger.warning( + 'Received a notification from Paypal for a tx %s that is duplicated in database.' % + txn_id + ) + + tx = self.browse(cr, uid, tx_ids[0], context=context) + + if parameters.get('notify_version')[0] != '2.6': + _logger.warning( + 'Received a notification from Paypal with version %s instead of 2.6. This could lead to issues when managing it.' % + parameters.get('notify_version') + ) + if parameters.get('test_ipn')[0]: + _logger.warning( + 'Received a notification from Paypal using sandbox' + ), + # check transaction + if parameters.get('payment_status')[0] != 'Completed': + invalid_parameters.append(('payment_status', 'Completed')) + # check what is buyed + if parameters.get('mc_gross')[0] != tx.amount: + invalid_parameters.append(('mc_gross', tx.amount)) + if parameters.get('mc_currency')[0] != tx.currency_id.name: + invalid_parameters.append(('mc_currency', tx.currency_id.name)) + # if parameters.get('payment_fee') != tx.payment_fee: + # invalid_parameters.append(('payment_fee', tx.payment_fee)) + # if parameters.get('quantity') != tx.quantity: + # invalid_parameters.append(('mc_currency', tx.quantity)) + # if parameters.get('shipping') != tx.shipping: + # invalid_parameters.append(('shipping', tx.shipping)) + # check buyer + # if parameters.get('payer_id') != tx.payer_id: + # invalid_parameters.append(('mc_gross', tx.payer_id)) + # if parameters.get('payer_email') != tx.payer_email: + # invalid_parameters.append(('payer_email', tx.payer_email)) + # check seller + # if parameters.get('receiver_email') != tx.receiver_email: + # invalid_parameters.append(('receiver_email', tx.receiver_email)) + # if parameters.get('receiver_id') != tx.receiver_id: + # invalid_parameters.append(('receiver_id', tx.receiver_id)) + + if not invalid_parameters: + self.write(cr, uid, [tx.id], { + 'payment_type': parameters.get('payment_type')[0], + 'date_validate': parameters.get('payment_date', [fields.datetime.now()])[0], + 'txn_type': parameters.get('express_checkout')[0], + }, context=context) + return tx.id + else: + _warn_message = 'The following transaction parameters are incorrect:\n' + for item in invalid_parameters: + _warn_message += '\t%s: received %s instead of %s\n' % (item[0], parameters.get(item[0])[0], item[1]) + _logger.warning(_warn_message) + + return False + + def create_paypal_command(self, cr, uid, cmd, parameters): + parameters.update(cmd=cmd) + return requests.post(self._paypal_url, data=parameters) + + def _validate_paypal(self, cr, uid, ids, context=None): + res = [] + for tx in self.browse(cr, uid, ids, context=context): + parameters = {} + parameters.update( + cmd='_notify-validate', + business='tdelavallee-facilitator@gmail.com', + item_name="%s %s" % ('cacapoutch', tx.reference), + item_number=tx.reference, + amount=tx.amount, + currency_code=tx.currency_id.name, + ) + print '\t', parameters + # paypal_url = "https://www.paypal.com/cgi-bin/webscr" + paypal_url = "https://www.sandbox.paypal.com/cgi-bin/webscr" + resp = requests.post(paypal_url, data=parameters) + print resp + print resp.url + print resp.text + response = urlparse.parse_qsl(resp) + print response + # transaction's unique id + # response["txn_id"] + + # "Failed", "Reversed", "Refunded", "Canceled_Reversal", "Denied" + status = "refused" + retry_time = False + + if response["payment_status"] == "Voided": + status = "refused" + elif response["payment_status"] in ("Completed", "Processed") and response["item_number"] == tx.reference and response["mc_gross"] == tx.amount: + status = "validated" + elif response["payment_status"] in ("Expired", "Pending"): + status = "pending" + retry_time = 60 + + res.append( + (status, retry_time, "payment_status=%s&pending_reason=%s&reason_code=%s" % ( + response["payment_status"], + response.get("pending_reason"), + response.get("reason_code"))) + ) + return response + + def _transaction_feedback_paypal(self, **values): + print values + return True diff --git a/addons/payment_acquirer_paypal/tests/__init__.py b/addons/payment_acquirer_paypal/tests/__init__.py new file mode 100644 index 00000000000..2868b3261e1 --- /dev/null +++ b/addons/payment_acquirer_paypal/tests/__init__.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2012-TODAY OpenERP S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from . import test_paypal + +checks = [ + test_paypal, +] + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/payment_acquirer_paypal/tests/test_paypal.py b/addons/payment_acquirer_paypal/tests/test_paypal.py new file mode 100644 index 00000000000..bd6174930ab --- /dev/null +++ b/addons/payment_acquirer_paypal/tests/test_paypal.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Business Applications +# Copyright (c) 2012-TODAY OpenERP S.A. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.addons.payment_acquirer.tests.common import PaymentAcquirerCommon +from openerp.osv.orm import except_orm + +from lxml import objectify +# import requests +# import urlparse + + +class BasicPayment(PaymentAcquirerCommon): + + def test_10_paypal_basic(self): + pass + + def test_11_paypal_form(self): + cr, uid = self.cr, self.uid + context = {} + base_url = self.registry('ir.config_parameter').get_param(cr, uid, 'web.base.url') + # ogone_url = self.payment_acquirer._get_ogone_urls(cr, uid, [ogone_id], None, None)[ogone_id]['ogone_standard_order_url'] + + model, paypal_view_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'payment_acquirer_paypal', 'paypal_acquirer_button') + + # forgot some mandatory fields: should crash + with self.assertRaises(except_orm): + paypal_id = self.payment_acquirer.create( + cr, uid, { + 'name': 'paypal', + 'env': 'test', + 'view_template_id': paypal_view_id, + 'paypal_email_id': 'tde+paypal-facilitator@openerp.com', + }, context=context + ) + # tde+buyer@openerp.com + + # create a new paypal account + paypal_id = self.payment_acquirer.create( + cr, uid, { + 'name': 'paypal', + 'env': 'test', + 'view_template_id': paypal_view_id, + 'paypal_email_id': 'tde+paypal-facilitator@openerp.com', + 'paypal_username': 'tde+paypal-facilitator_api1.openerp.com', + }, context=context + ) + # verify acquirer data + paypal = self.payment_acquirer.browse(cr, uid, paypal_id, context) + self.assertEqual(paypal.env, 'test', 'test without test env') + + # render the button + res = self.payment_acquirer.render( + cr, uid, paypal_id, + 'test_ref0', 0.01, self.currency_euro, + partner_id=None, + partner_values=self.buyer_values, + context=context) + print res + + # # check some basic paypal methods + # res = self.payment_transaction.validate_paypal_notification( + # cr, uid, + # 'http://localhost/payment?mc_gross=19.95&protection_eligibility=Eligible&address_status=confirmed&payer_id=LPLWNMTBWMFAY&tax=0.00&address_street=1+Main+St&payment_date=20%3A12%3A59+Jan+13%2C+2009+PST&payment_status=Completed&charset=windows-1252&address_zip=95131&first_name=Test&mc_fee=0.88&address_country_code=US&address_name=Test+User¬ify_version=2.6&custom=&payer_status=verified&address_country=United+States&address_city=San+Jose&quantity=1&verify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtf&payer_email=gpmac_1231902590_per%40paypal.com&txn_id=61E67681CH3238416&payment_type=instant&last_name=User&address_state=CA&receiver_email=gpmac_1231902686_biz%40paypal.com&payment_fee=0.88&receiver_id=S8XGHLYDW9T3S&txn_type=express_checkout&item_name=&mc_currency=USD&item_number=&residence_country=US&test_ipn=1&handling_amount=0.00&transaction_subject=&payment_gross=19.95&shipping=0.00') + # self.assertEqual(res, False, 'payment: paypal validation on a txn_id that does not exists should return False') + + # txn_id = self.payment_transaction.create( + # cr, uid, { + # 'amount': 0.01, + # 'acquirer_id': paypal_id, + # 'currency_id': currency_id, + # 'reference': 'test_reference', + # 'paypal_txn_id': '61E67681CH3238416', + # }, context=context + # ) + # res = self.payment_transaction.validate_paypal_notification( + # cr, uid, + # 'http://localhost/payment?mc_gross=19.95&protection_eligibility=Eligible&address_status=confirmed&payer_id=LPLWNMTBWMFAY&tax=0.00&address_street=1+Main+St&payment_date=20%3A12%3A59+Jan+13%2C+2009+PST&payment_status=Completed&charset=windows-1252&address_zip=95131&first_name=Test&mc_fee=0.88&address_country_code=US&address_name=Test+User¬ify_version=2.6&custom=&payer_status=verified&address_country=United+States&address_city=San+Jose&quantity=1&verify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtf&payer_email=gpmac_1231902590_per%40paypal.com&txn_id=61E67681CH3238416&payment_type=instant&last_name=User&address_state=CA&receiver_email=gpmac_1231902686_biz%40paypal.com&payment_fee=0.88&receiver_id=S8XGHLYDW9T3S&txn_type=express_checkout&item_name=&mc_currency=USD&item_number=&residence_country=US&test_ipn=1&handling_amount=0.00&transaction_subject=&payment_gross=19.95&shipping=0.00') + # print res + + # # user pays using Paypal + # resp = self.payment_transaction.create_paypal_command( + # cr, uid, cmd='_xclick', parameters={ + # 'business': 'tdelavallee-facilitator@gmail.com', + # 'amount': 50, + # 'item_name': 'test_item', + # 'quantity': 1, + # 'currency_code': 'USD', + # 'return': 'http://www.example.com', + # }) + # print resp + # print resp.url + # print resp.text + + # self.payment_transaction.validate(cr, uid, [tx_id], context=context) diff --git a/addons/payment_acquirer_paypal/views/paypal.xml b/addons/payment_acquirer_paypal/views/paypal.xml new file mode 100644 index 00000000000..105a8595057 --- /dev/null +++ b/addons/payment_acquirer_paypal/views/paypal.xml @@ -0,0 +1,34 @@ + + + + + + + + diff --git a/addons/website_payment/__openerp__.py b/addons/website_payment/__openerp__.py index 4368363c312..05fe44a9260 100644 --- a/addons/website_payment/__openerp__.py +++ b/addons/website_payment/__openerp__.py @@ -27,7 +27,7 @@ 'description': """ """, 'author': 'OpenERP SA', - 'depends': ['website', 'payment_acquirer'], + 'depends': ['website', 'payment_acquirer', 'payment_acquirer_ogone', 'payment_acquirer_paypal'], 'data': [ 'views/website_payment_templates.xml', ], diff --git a/addons/website_payment/views/website_payment_templates.xml b/addons/website_payment/views/website_payment_templates.xml index 03b045bf716..924be2ed848 100644 --- a/addons/website_payment/views/website_payment_templates.xml +++ b/addons/website_payment/views/website_payment_templates.xml @@ -1,42 +1,51 @@ - + - + + + Paypal (Test) + /payment/paypal/test + + 50 + + + + + Ogone (Test) + /payment/ogone/test + + 50 + + - - + - -- cgit v1.2.3