From 9324e026d566288a70bcd98abc182ed4d2f5a4f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 15 Nov 2013 18:07:07 +0100 Subject: [PATCH] [TEST] payment_acquirer_ogone: added / cleaned tests + some code cleaning bzr revid: tde@openerp.com-20131115170707-3godrd7b7aht14j7 --- addons/payment_acquirer_ogone/models/ogone.py | 77 +++++---- .../static/src/img/icon_code.png | Bin 973 -> 0 bytes .../static/src/img/icon_credit_card.png | Bin 3408 -> 0 bytes .../tests/test_ogone.py | 157 +++++++++++------- 4 files changed, 144 insertions(+), 90 deletions(-) delete mode 100644 addons/payment_acquirer_ogone/static/src/img/icon_code.png delete mode 100644 addons/payment_acquirer_ogone/static/src/img/icon_credit_card.png diff --git a/addons/payment_acquirer_ogone/models/ogone.py b/addons/payment_acquirer_ogone/models/ogone.py index 7f501ec2e4f..fd3c3f55ee4 100644 --- a/addons/payment_acquirer_ogone/models/ogone.py +++ b/addons/payment_acquirer_ogone/models/ogone.py @@ -161,41 +161,16 @@ class PaymentTxOgone(osv.Model): 'ogone_feedback_model': fields.char(), 'ogone_feedback_eval': fields.char(), 'ogone_complus': fields.char('Complus'), + 'ogone_payid': fields.char() } # -------------------------------------------------- # 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']._ogone_generate_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): + """ Ogone-specific value generation for rendering a transaction-based + form button. """ tx = self.browse(cr, uid, id, context=context) tx_data = { @@ -217,16 +192,51 @@ class PaymentTxOgone(osv.Model): context=context ) + def _ogone_form_get_tx_from_shasign_out(self, cr, uid, data, context=None): + """ Given a data dict coming from ogone, verify it and find the related + transaction record. """ + 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 %s' % (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']._ogone_generate_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_feedback(self, cr, uid, data, context=None): - print '-- ogone: ogone_form_feedback' tx = self._ogone_form_get_tx_from_shasign_out(cr, uid, data, context) if not tx: raise ValidationError('Ogone: feedback: tx not found') + if tx.state == 'done': + _logger.warning('Ogone: trying to validate an already validated tx (ref %s' % tx.reference) + return False status = int(data.get('STATUS', '0')) - print '\togone: statuts %s' % status if status in [5, 9]: - tx.write({'state': 'done'}) + tx.write({ + 'state': 'done', + 'date_validate': data['TRXDATE'], + 'ogone_payid': data['PAYID'], + }) return True else: error = 'Ogone: feedback error: %(error_str)s\n\n%(error_code)s: %(error_msg)s' % { @@ -324,6 +334,9 @@ class PaymentTxOgone(osv.Model): tx_data['SHASIGN'] = shasign return tx_data + def ogone_s2s_feedback(self, cr, uid, data, context=None): + pass + def ogone_s2s_execute(self, cr, uid, id, values, context=None): tx = self.browse(cr, uid, id, context=context) @@ -400,4 +413,4 @@ class PaymentTxOgone(osv.Model): if not tx_done and tries == 0: raise Exception('Cannot get transaction status...') - return (tx_status, orderid, payid) + return tx_status diff --git a/addons/payment_acquirer_ogone/static/src/img/icon_code.png b/addons/payment_acquirer_ogone/static/src/img/icon_code.png deleted file mode 100644 index 49bbab035b1d510dac4c66f4f68c1c66cd914cc5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 973 zcmV;;12X)HP)@B5bT_n(Jh^i66fx?qK5JTv-ShThA z+AvaQHkX$l-8b+b3?T%hU9@2u<_W@xqzYCsdjzOXAy-zOplBBngn@>jRD>`H!CV&n ztk=` zVWBC#uT?~>a~SF)5b78l+UFxhjtZ0PSQ5_u0p6{xK@xS4&5jWeA5j!hbZlI_d#Fw)H3h&`+Zm`3MrpON*mYjAU#EhOlfNCmbAd_!6p5$=OFlAgT;6H z9%?jqg8>dq!v{w}i&m?p?=m>n zo1U%=!Af|ZOR}PdwTr2#DNxfS<37GNn{dk$F#0aMFW)f8j-uS@7Ul2ND};%j<6wP# zof^#^rO3q2P8Eeh0W&i**xK5n=N-(?&!b+iV`F0jrBVsKUJpx4OUUJNXfztAR;wB~ z>n|)UXds-sy4ypw9-y4gz#`D<-~boe-SHDZ*t>uZ&zC20@9tf+cRN(k^2lpW;oRJu zHp<3%uU4yJY>ZM%-cC$TXt_yVvkn7s9T&De9)Dj(Mo`HWaE|1BB+x0@Xj08=l}q1G zMZ}?Kw3=;Xb9tHt+8nZHn8Iu}uM3$|z{4iS@jl<>nqs+Bn!x1bd40zav2LT;fTHeg z&QwrTL*7v|3`*~AW~48VgJ1*QZWkXveHvD}IAX+K*(v_8p?+&hG3_9zXT&l^NW{dK zQDmHo2z%lC1HHyv`&S^R+sBCxM(jB}Po+|ET{4(IwOdm+odo_#sFxs$Q~Y0iYYu^^ zvj)LM&Z)`h&KV{DKf>hDBr#=5lDtnpMLF)GNq_nkIltfCAKY5J`9o4;diZwj%^lbA vLMhF}fDkK1-}9_%H?DuUdgaRdp8^a3wp!3p`auMp00000NkvXXu0mjfw?WC& diff --git a/addons/payment_acquirer_ogone/static/src/img/icon_credit_card.png b/addons/payment_acquirer_ogone/static/src/img/icon_credit_card.png deleted file mode 100644 index 91c266d207b1284f5d17d568aa851e19be625bec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3408 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 mio$tEEH!z4iEIA<1Q-B%n{V}ajqKV00000 -# -# 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.tests.common import PaymentAcquirerCommon from openerp.addons.payment_acquirer_ogone.controllers.main import OgoneController -# from openerp.osv.orm import except_orm +from openerp.tools import mute_logger from lxml import objectify # import requests +import time import urlparse -class BasicPayment(PaymentAcquirerCommon): +class OgonePayment(PaymentAcquirerCommon): - def test_20_ogone_form(self): + def setUp(self): + super(OgonePayment, self).setUp() 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') + self.base_url = self.registry('ir.config_parameter').get_param(cr, uid, 'web.base.url') + model, self.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( + self.ogone_id = self.payment_acquirer.create( cr, uid, { 'name': 'ogone', 'env': 'test', - 'view_template_id': ogone_view_id, + 'view_template_id': self.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.ogone_url = self.payment_acquirer._get_ogone_urls(cr, uid, [self.ogone_id], None, None)[self.ogone_id]['ogone_standard_order_url'] + + def test_00_ogone_acquirer(self): + ogone = self.payment_acquirer.browse(self.cr, self.uid, self.ogone_id, None) self.assertEqual(ogone.env, 'test', 'test without test env') + def test_10_ogone_form_render(self): + cr, uid, context = self.cr, self.uid, {} + + # ---------------------------------------- + # Test: button direct rendering + shasign + # ---------------------------------------- + form_values = { 'PSPID': 'pinky', 'ORDERID': 'test_ref0', @@ -69,19 +59,20 @@ class BasicPayment(PaymentAcquirerCommon): 'OWNERTOWN': 'Sin City', 'OWNERTELNO': '0032 12 34 56 78', 'SHASIGN': '815f67b8ff70d234ffcf437c13a9fa7f807044cc', - 'ACCEPTURL': '%s' % urlparse.urljoin(base_url, OgoneController._accept_url), - 'DECLINEURL': '%s' % urlparse.urljoin(base_url, OgoneController._decline_url), - 'EXCEPTIONURL': '%s' % urlparse.urljoin(base_url, OgoneController._exception_url), - 'CANCELURL': '%s' % urlparse.urljoin(base_url, OgoneController._cancel_url), + 'ACCEPTURL': '%s' % urlparse.urljoin(self.base_url, OgoneController._accept_url), + 'DECLINEURL': '%s' % urlparse.urljoin(self.base_url, OgoneController._decline_url), + 'EXCEPTIONURL': '%s' % urlparse.urljoin(self.base_url, OgoneController._exception_url), + 'CANCELURL': '%s' % urlparse.urljoin(self.base_url, OgoneController._cancel_url), } # render the button res = self.payment_acquirer.render( - cr, uid, ogone_id, + cr, uid, self.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') @@ -93,13 +84,16 @@ class BasicPayment(PaymentAcquirerCommon): 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) + + # ---------------------------------------- + # Test2: button using tx + validation + # ---------------------------------------- # create a new draft tx tx_id = self.payment_transaction.create( cr, uid, { 'amount': 0.01, - 'acquirer_id': ogone_id, + 'acquirer_id': self.ogone_id, 'currency_id': self.currency_euro_id, 'reference': 'test_ref0', 'partner_id': self.buyer_id, @@ -107,12 +101,13 @@ class BasicPayment(PaymentAcquirerCommon): ) # render the button res = self.payment_acquirer.render( - cr, uid, ogone_id, - 'test_ref0', 0.01, self.currency_euro, + cr, uid, self.ogone_id, + 'should_be_erased', 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') @@ -125,33 +120,78 @@ class BasicPayment(PaymentAcquirerCommon): '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 = {} + @mute_logger('openerp.addons.payment_acquirer_ogone.models.ogone', 'ValidationError') + def test_20_ogone_form_management(self): + cr, uid, context = self.cr, self.uid, {} - model, ogone_view_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'payment_acquirer_ogone', 'ogone_acquirer_button') + # typical data posted by ogone after client has successfully paid + ogone_post_data = { + 'orderID': u'test_ref_2', + 'STATUS': u'9', + 'CARDNO': u'XXXXXXXXXXXX0002', + 'PAYID': u'25381582', + 'CN': u'Norbert Buyer', + 'NCERROR': u'0', + 'TRXDATE': u'11/15/13', + 'IP': u'85.201.233.72', + 'BRAND': u'VISA', + 'ACCEPTANCE': u'test123', + 'currency': u'EUR', + 'amount': u'1.95', + 'SHASIGN': u'7B7B0ED9CBC4A85543A9073374589033A62A05A5', + 'ED': u'0315', + 'PM': u'CreditCard' + } - # create a new ogone account - ogone_id = self.payment_acquirer.create( + # should raise error about unknown tx + with self.assertRaises(ValidationError): + self.payment_transaction.ogone_form_feedback(cr, uid, ogone_post_data, context=context) + + # create tx + tx_id = self.payment_transaction.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', + 'amount': 1.95, + 'acquirer_id': self.ogone_id, + 'currency_id': self.currency_euro_id, + 'reference': 'test_ref_2', + 'partner_name': 'Norbert Buyer', }, context=context ) + # validate it + self.payment_transaction.ogone_form_feedback(cr, uid, ogone_post_data, context=context) + # check state + tx = self.payment_transaction.browse(cr, uid, tx_id, context=context) + self.assertEqual(tx.state, 'done', 'ogone: validation did not put tx into done state') + self.assertEqual(tx.ogone_payid, ogone_post_data.get('PAYID'), 'ogone: validation did not update tx payid') + + # reset tx + tx.write({'state': 'draft', 'date_validate': False, 'ogone_payid': False}) + + # now ogone post is ok: try to modify the SHASIGN + ogone_post_data['SHASIGN'] = 'a4c16bae286317b82edb49188d3399249a784691' + with self.assertRaises(ValidationError): + self.payment_transaction.ogone_form_feedback(cr, uid, ogone_post_data, context=context) + + # simulate an error + ogone_post_data['STATUS'] = 2 + ogone_post_data['SHASIGN'] = 'a4c16bae286317b82edb49188d3399249a784691' + self.payment_transaction.ogone_form_feedback(cr, uid, ogone_post_data, context=context) + # check state + tx = self.payment_transaction.browse(cr, uid, tx_id, context=context) + self.assertEqual(tx.state, 'error', 'ogone: erroneous validation did not put tx into error state') + + def test_30_ogone_s2s(self): + test_ref = 'test_ref_%.15f' % time.time() + print test_ref + cr, uid, context = self.cr, self.uid, {} # create a new draft tx tx_id = self.payment_transaction.create( cr, uid, { 'amount': 0.01, - 'acquirer_id': ogone_id, + 'acquirer_id': self.ogone_id, 'currency_id': self.currency_euro_id, - 'reference': 'test_ogone_0', + 'reference': test_ref, 'partner_id': self.buyer_id, 'type': 'server2server', }, context=context @@ -162,11 +202,12 @@ class BasicPayment(PaymentAcquirerCommon): 'expiry_date_mm': '01', 'expiry_date_yy': '2015', 'holder_name': 'Norbert Poilu', - 'number': '4000000000000002', + 'number': '4000000000000006', 'brand': 'VISA', }, context=context) print res + print '--------------------' res = self.payment_transaction.ogone_s2s_execute(cr, uid, tx_id, {}, context=context) print res