From 356c93a42dc5a7e5aa902fafa18cddea23dd16a6 Mon Sep 17 00:00:00 2001 From: Barad Mahendrasinh Date: Fri, 26 Mar 2021 18:08:13 +0100 Subject: [PATCH] [REF] payment_buckaroo: migrate Buckaroo to the new payment API See the merge commit for more details. task-2333032 --- addons/payment_buckaroo/__init__.py | 11 +- addons/payment_buckaroo/__manifest__.py | 7 +- .../payment_buckaroo/controllers/__init__.py | 2 +- addons/payment_buckaroo/controllers/main.py | 28 +-- .../data/payment_acquirer_data.xml | 18 +- addons/payment_buckaroo/models/__init__.py | 5 +- addons/payment_buckaroo/models/payment.py | 192 ------------------ .../models/payment_acquirer.py | 65 ++++++ .../models/payment_transaction.py | 144 +++++++++++++ addons/payment_buckaroo/tests/__init__.py | 3 +- addons/payment_buckaroo/tests/common.py | 19 ++ .../payment_buckaroo/tests/test_buckaroo.py | 182 +++++------------ .../views/payment_buckaroo_templates.xml | 41 ++-- .../payment_buckaroo/views/payment_views.xml | 28 ++- 14 files changed, 344 insertions(+), 401 deletions(-) delete mode 100644 addons/payment_buckaroo/models/payment.py create mode 100644 addons/payment_buckaroo/models/payment_acquirer.py create mode 100644 addons/payment_buckaroo/models/payment_transaction.py create mode 100644 addons/payment_buckaroo/tests/common.py diff --git a/addons/payment_buckaroo/__init__.py b/addons/payment_buckaroo/__init__.py index 7632661d918..43c88da84e3 100644 --- a/addons/payment_buckaroo/__init__.py +++ b/addons/payment_buckaroo/__init__.py @@ -1,10 +1,11 @@ -# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import models from . import controllers -from odoo.addons.payment.models.payment_acquirer import create_missing_journal_for_acquirers -from odoo.addons.payment import reset_payment_provider +from . import models + +from odoo.addons.payment import reset_payment_acquirer +from odoo.addons.payment.models.payment_acquirer import create_missing_journals # post-init hook + def uninstall_hook(cr, registry): - reset_payment_provider(cr, registry, 'buckaroo') + reset_payment_acquirer(cr, registry, 'buckaroo') diff --git a/addons/payment_buckaroo/__manifest__.py b/addons/payment_buckaroo/__manifest__.py index 7b25b9ede55..1be68e8b107 100644 --- a/addons/payment_buckaroo/__manifest__.py +++ b/addons/payment_buckaroo/__manifest__.py @@ -1,11 +1,11 @@ -# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. { 'name': 'Buckaroo Payment Acquirer', + 'version': '2.0', 'category': 'Accounting/Payment Acquirers', 'sequence': 355, 'summary': 'Payment Acquirer: Buckaroo Implementation', - 'version': '1.0', 'description': """Buckaroo Payment Acquirer""", 'depends': ['payment'], 'data': [ @@ -13,8 +13,7 @@ 'views/payment_buckaroo_templates.xml', 'data/payment_acquirer_data.xml', ], - 'installable': True, 'application': True, - 'post_init_hook': 'create_missing_journal_for_acquirers', + 'post_init_hook': 'create_missing_journals', 'uninstall_hook': 'uninstall_hook', } diff --git a/addons/payment_buckaroo/controllers/__init__.py b/addons/payment_buckaroo/controllers/__init__.py index 65a8c12013d..80ee4da1c5e 100644 --- a/addons/payment_buckaroo/controllers/__init__.py +++ b/addons/payment_buckaroo/controllers/__init__.py @@ -1,3 +1,3 @@ -# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. from . import main diff --git a/addons/payment_buckaroo/controllers/main.py b/addons/payment_buckaroo/controllers/main.py index 1810355cc3c..4c845fdc607 100644 --- a/addons/payment_buckaroo/controllers/main.py +++ b/addons/payment_buckaroo/controllers/main.py @@ -1,7 +1,8 @@ -# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. import logging import pprint + import werkzeug from odoo import http @@ -12,20 +13,13 @@ _logger = logging.getLogger(__name__) class BuckarooController(http.Controller): _return_url = '/payment/buckaroo/return' - _cancel_url = '/payment/buckaroo/cancel' - _exception_url = '/payment/buckaroo/error' - _reject_url = '/payment/buckaroo/reject' - @http.route([ - '/payment/buckaroo/return', - '/payment/buckaroo/cancel', - '/payment/buckaroo/error', - '/payment/buckaroo/reject', - ], type='http', auth='public', csrf=False) - def buckaroo_return(self, **post): - """ Buckaroo.""" - _logger.info('Buckaroo: entering form_feedback with post data %s', pprint.pformat(post)) # debug - request.env['payment.transaction'].sudo().form_feedback(post, 'buckaroo') - post = {key.upper(): value for key, value in post.items()} - return_url = post.get('ADD_RETURNDATA') or '/' - return werkzeug.utils.redirect('/payment/process') + @http.route(_return_url, type='http', auth='public', methods=['POST'], csrf=False) + def buckaroo_return_from_redirect(self, **data): + """ Process the data returned by Buckaroo after redirection. + + :param dict data: The feedback data + """ + _logger.info("received notification data:\n%s", pprint.pformat(data)) + request.env['payment.transaction'].sudo()._handle_feedback_data('buckaroo', data) + return werkzeug.utils.redirect('/payment/status') diff --git a/addons/payment_buckaroo/data/payment_acquirer_data.xml b/addons/payment_buckaroo/data/payment_acquirer_data.xml index 50b74354f00..09c7ce95945 100644 --- a/addons/payment_buckaroo/data/payment_acquirer_data.xml +++ b/addons/payment_buckaroo/data/payment_acquirer_data.xml @@ -1,14 +1,12 @@ - - + - - Buckaroo - - buckaroo - - - + + buckaroo + + False + False + False + - diff --git a/addons/payment_buckaroo/models/__init__.py b/addons/payment_buckaroo/models/__init__.py index ef12533682f..8e04237423a 100644 --- a/addons/payment_buckaroo/models/__init__.py +++ b/addons/payment_buckaroo/models/__init__.py @@ -1,3 +1,4 @@ -# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import payment +from . import payment_acquirer +from . import payment_transaction diff --git a/addons/payment_buckaroo/models/payment.py b/addons/payment_buckaroo/models/payment.py deleted file mode 100644 index 8689cf5f2ba..00000000000 --- a/addons/payment_buckaroo/models/payment.py +++ /dev/null @@ -1,192 +0,0 @@ -# coding: utf-8 -from hashlib import sha1 -import logging - -from werkzeug import urls - -from odoo import api, fields, models, _ -from odoo.addons.payment.models.payment_acquirer import ValidationError -from odoo.addons.payment_buckaroo.controllers.main import BuckarooController - -from odoo.tools.float_utils import float_compare - -_logger = logging.getLogger(__name__) - - -def normalize_keys_upper(data): - """Set all keys of a dictionnary to uppercase - - Buckaroo parameters names are case insensitive - convert everything to upper case to be able to easily detected the presence - of a parameter by checking the uppercase key only - """ - return {key.upper(): val for key, val in data.items()} - - -class AcquirerBuckaroo(models.Model): - _inherit = 'payment.acquirer' - - provider = fields.Selection(selection_add=[ - ('buckaroo', 'Buckaroo') - ], ondelete={'buckaroo': 'set default'}) - brq_websitekey = fields.Char('WebsiteKey', required_if_provider='buckaroo', groups='base.group_user') - brq_secretkey = fields.Char('SecretKey', required_if_provider='buckaroo', groups='base.group_user') - - def _get_buckaroo_urls(self, environment): - """ Buckaroo URLs - """ - if environment == 'prod': - return { - 'buckaroo_form_url': 'https://checkout.buckaroo.nl/html/', - } - else: - return { - 'buckaroo_form_url': 'https://testcheckout.buckaroo.nl/html/', - } - - def _buckaroo_generate_digital_sign(self, 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' (odoo contacting buckaroo) or 'out' (buckaroo - contacting odoo). - :param dict values: transaction values - - :return string: shasign - """ - assert inout in ('in', 'out') - assert self.provider == 'buckaroo' - - keys = "add_returndata Brq_amount Brq_culture Brq_currency Brq_invoicenumber Brq_return Brq_returncancel Brq_returnerror Brq_returnreject brq_test Brq_websitekey".split() - - def get_value(key): - if values.get(key): - return values[key] - return '' - - values = dict(values or {}) - - if inout == 'out': - for key in list(values): - # case insensitive keys - if key.upper() == 'BRQ_SIGNATURE': - del values[key] - break - - items = sorted(values.items(), key=lambda pair: pair[0].lower()) - sign = ''.join('%s=%s' % (k, urls.url_unquote_plus(v)) for k, v in items) - else: - sign = ''.join('%s=%s' % (k, get_value(k)) for k in keys) - # Add the pre-shared secret key at the end of the signature - sign = sign + self.brq_secretkey - shasign = sha1(sign.encode('utf-8')).hexdigest() - return shasign - - def buckaroo_form_generate_values(self, values): - base_url = self.get_base_url() - buckaroo_tx_values = dict(values) - buckaroo_tx_values.update({ - 'Brq_websitekey': self.brq_websitekey, - 'Brq_amount': values['amount'], - 'Brq_currency': values['currency'] and values['currency'].name or '', - 'Brq_invoicenumber': values['reference'], - 'brq_test': True if self.state == 'test' else False, - 'Brq_return': urls.url_join(base_url, BuckarooController._return_url), - 'Brq_returncancel': urls.url_join(base_url, BuckarooController._cancel_url), - 'Brq_returnerror': urls.url_join(base_url, BuckarooController._exception_url), - 'Brq_returnreject': urls.url_join(base_url, BuckarooController._reject_url), - 'Brq_culture': (values.get('partner_lang') or 'en_US').replace('_', '-'), - 'add_returndata': buckaroo_tx_values.pop('return_url', '') or '', - }) - buckaroo_tx_values['Brq_signature'] = self._buckaroo_generate_digital_sign('in', buckaroo_tx_values) - return buckaroo_tx_values - - def buckaroo_get_form_action_url(self): - self.ensure_one() - environment = 'prod' if self.state == 'enabled' else 'test' - return self._get_buckaroo_urls(environment)['buckaroo_form_url'] - - -class TxBuckaroo(models.Model): - _inherit = 'payment.transaction' - - # buckaroo status - _buckaroo_valid_tx_status = [190] - _buckaroo_pending_tx_status = [790, 791, 792, 793] - _buckaroo_cancel_tx_status = [890, 891] - _buckaroo_error_tx_status = [490, 491, 492] - _buckaroo_reject_tx_status = [690] - - # -------------------------------------------------- - # FORM RELATED METHODS - # -------------------------------------------------- - - @api.model - def _buckaroo_form_get_tx_from_data(self, data): - """ Given a data dict coming from buckaroo, verify it and find the related - transaction record. """ - origin_data = dict(data) - data = normalize_keys_upper(data) - reference, pay_id, shasign = data.get('BRQ_INVOICENUMBER'), data.get('BRQ_PAYMENT'), data.get('BRQ_SIGNATURE') - if not reference or not pay_id or not shasign: - error_msg = _('Buckaroo: received data with missing reference (%s) or pay_id (%s) or shasign (%s)') % (reference, pay_id, shasign) - _logger.info(error_msg) - raise ValidationError(error_msg) - - tx = self.search([('reference', '=', reference)]) - if not tx or len(tx) > 1: - error_msg = _('Buckaroo: received data for reference %s') % (reference) - if not tx: - error_msg += _('; no order found') - else: - error_msg += _('; multiple order found') - _logger.info(error_msg) - raise ValidationError(error_msg) - - # verify shasign - shasign_check = tx.acquirer_id._buckaroo_generate_digital_sign('out', origin_data) - if shasign_check.upper() != shasign.upper(): - error_msg = _('Buckaroo: invalid shasign, received %s, computed %s, for data %s') % (shasign, shasign_check, data) - _logger.info(error_msg) - raise ValidationError(error_msg) - - return tx - - def _buckaroo_form_get_invalid_parameters(self, data): - invalid_parameters = [] - data = normalize_keys_upper(data) - if self.acquirer_reference and data.get('BRQ_TRANSACTIONS') != self.acquirer_reference: - invalid_parameters.append(('Transaction Id', data.get('BRQ_TRANSACTIONS'), self.acquirer_reference)) - # check what is buyed - if float_compare(float(data.get('BRQ_AMOUNT', '0.0')), self.amount, 2) != 0: - invalid_parameters.append(('Amount', data.get('BRQ_AMOUNT'), '%.2f' % self.amount)) - if data.get('BRQ_CURRENCY') != self.currency_id.name: - invalid_parameters.append(('Currency', data.get('BRQ_CURRENCY'), self.currency_id.name)) - - return invalid_parameters - - def _buckaroo_form_validate(self, data): - data = normalize_keys_upper(data) - status_code = int(data.get('BRQ_STATUSCODE', '0')) - if status_code in self._buckaroo_valid_tx_status: - self.write({'acquirer_reference': data.get('BRQ_TRANSACTIONS')}) - self._set_transaction_done() - return True - elif status_code in self._buckaroo_pending_tx_status: - self.write({'acquirer_reference': data.get('BRQ_TRANSACTIONS')}) - self._set_transaction_pending() - return True - elif status_code in self._buckaroo_cancel_tx_status: - self.write({'acquirer_reference': data.get('BRQ_TRANSACTIONS')}) - self._set_transaction_cancel() - return True - else: - error = 'Buckaroo: feedback error' - _logger.info(error) - self.write({ - 'state_message': error, - 'acquirer_reference': data.get('BRQ_TRANSACTIONS'), - }) - self._set_transaction_cancel() - return False diff --git a/addons/payment_buckaroo/models/payment_acquirer.py b/addons/payment_buckaroo/models/payment_acquirer.py new file mode 100644 index 00000000000..5b298e8401e --- /dev/null +++ b/addons/payment_buckaroo/models/payment_acquirer.py @@ -0,0 +1,65 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from hashlib import sha1 + +from werkzeug import urls + +from odoo import fields, models + + +class PaymentAcquirer(models.Model): + _inherit = 'payment.acquirer' + + provider = fields.Selection( + selection_add=[('buckaroo', "Buckaroo")], ondelete={'buckaroo': 'set default'}) + buckaroo_website_key = fields.Char( + string="Website Key", help="The key solely used to identify the website with Buckaroo", + required_if_provider='buckaroo') + buckaroo_secret_key = fields.Char( + string="Buckaroo Secret Key", required_if_provider='buckaroo', groups='base.group_system') + + def _buckaroo_get_api_url(self): + """ Return the API URL according to the state. + + Note: self.ensure_one() + + :return: The API URL + :rtype: str + """ + self.ensure_one() + if self.state == 'enabled': + return 'https://checkout.buckaroo.nl/html/' + else: + return 'https://testcheckout.buckaroo.nl/html/' + + def _buckaroo_generate_digital_sign(self, values, incoming=True): + """ Generate the shasign for incoming or outgoing communications. + + :param dict values: The values used to generate the signature + :param bool incoming: Whether the signature must be generate for an incoming (Buckaroo to + Odoo) or outgoing (Odoo to Buckaroo) communication. + :return: The shasign + :rtype: str + """ + if incoming: + # Remove the signature from the values used to check the signature + for key in values.keys(): + if key.upper() == 'BRQ_SIGNATURE': # Keys are case-insensitive + del values[key] + break + # Incoming communication values must be URL-decoded before checking the signature + items = [(k, urls.url_unquote_plus(v)) for k, v in values.items()] + else: + # Only use items whose key starts with 'add_', 'brq_', or 'cust_' (case insensitive) + items = [ + (k, v) for k, v in values.items() + if any(k.upper().startswith(key_prefix) for key_prefix in ('ADD_', 'BRQ_', 'CUST_')) + ] + # Sort parameters by upper-cased key name + sorted_items = sorted(items, key=lambda pair: pair[0].upper()) + # Build the signing string by concatenating all parameters + sign_string = ''.join(f'{k}={v or ""}' for k, v in sorted_items) + # Append the pre-shared secret key to the signing string + sign_string += self.buckaroo_secret_key + # Calculate the SHA-1 hash over the signing string + return sha1(sign_string.encode('utf-8')).hexdigest() diff --git a/addons/payment_buckaroo/models/payment_transaction.py b/addons/payment_buckaroo/models/payment_transaction.py new file mode 100644 index 00000000000..89c3d354526 --- /dev/null +++ b/addons/payment_buckaroo/models/payment_transaction.py @@ -0,0 +1,144 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import logging + +from werkzeug import urls + +from odoo import _, api, models +from odoo.exceptions import ValidationError + +from odoo.addons.payment_buckaroo.controllers.main import BuckarooController + +_logger = logging.getLogger(__name__) + + +def _normalize_dataset(data): + """ Set all keys of a dictionary to uppercase. + + As Buckaroo parameters names are case insensitive, we can convert everything to upper case to + easily detected the presence of a parameter by checking the uppercase key only. + + :param dict data: The dictionary whose keys must be set to uppercase + :return: A copy of the original data with all keys set to uppercase + :rtype: dict + """ + return {key.upper(): val for key, val in data.items()} + + +class PaymentTransaction(models.Model): + _inherit = 'payment.transaction' + + # Buckaroo status codes + _pending_tx_status = [790, 791, 792, 793] + _valid_tx_status = [190] + _cancel_tx_status = [890, 891] + + def _get_specific_rendering_values(self, processing_values): + """ Override of payment to return Buckaroo-specific rendering values. + + Note: self.ensure_one() from `_get_processing_values` + + :param dict processing_values: The generic and specific processing values of the transaction + :return: The dict of acquirer-specific processing values + :rtype: dict + """ + res = super()._get_specific_rendering_values(processing_values) + if self.provider != 'buckaroo': + return res + + return_url = urls.url_join(self.acquirer_id._get_base_url(), BuckarooController._return_url) + rendering_values = { + 'api_url': self.acquirer_id._buckaroo_get_api_url(), + 'Brq_websitekey': self.acquirer_id.buckaroo_website_key, + 'Brq_amount': self.amount, + 'Brq_currency': self.currency_id.name, + 'Brq_invoicenumber': self.reference, + # Include all 4 URL keys despite they share the same value as they are part of the sig. + 'Brq_return': return_url, + 'Brq_returncancel': return_url, + 'Brq_returnerror': return_url, + 'Brq_returnreject': return_url, + } + if self.partner_lang: + rendering_values['Brq_culture'] = self.partner_lang.replace('_', '-') + rendering_values['Brq_signature'] = self.acquirer_id._buckaroo_generate_digital_sign( + rendering_values, incoming=False + ) + return rendering_values + + @api.model + def _get_tx_from_feedback_data(self, provider, data): + """ Override of payment to find the transaction based on Buckaroo data. + + :param str provider: The provider of the acquirer that handled the transaction + :param dict data: The feedback data sent by the provider + :return: The transaction if found + :rtype: recordset of `payment.transaction` + :raise: ValidationError if inconsistent data were received + :raise: ValidationError if the data match no transaction + :raise: ValidationError if the signature can not be verified + """ + tx = super()._get_tx_from_feedback_data(provider, data) + if provider != 'buckaroo': + return tx + + normalized_data = _normalize_dataset(data) + reference = normalized_data.get('BRQ_INVOICENUMBER') + shasign = normalized_data.get('BRQ_SIGNATURE') + if not reference or not shasign: + raise ValidationError( + "Buckaroo: " + _( + "Received data with missing reference (%(ref)s) or shasign (%(sign))", + ref=reference, sign=shasign + ) + ) + + tx = self.search([('reference', '=', reference), ('provider', '=', 'buckaroo')]) + if not tx: + raise ValidationError( + "Buckaroo: " + _("No transaction found matching reference %s.", reference) + ) + + # Verify signature + shasign_check = tx.acquirer_id._buckaroo_generate_digital_sign(data, incoming=True) + if shasign_check != shasign: + raise ValidationError( + "Buckaroo: " + _( + "Invalid shasign: received %(sign)s, computed %(check)s", + sign=shasign, check=shasign_check + ) + ) + + return tx + + def _process_feedback_data(self, data): + """ Override of payment to process the transaction based on Buckaroo data. + + Note: self.ensure_one() + + :param dict data: The feedback data sent by the provider + :return: None + :raise: ValidationError if inconsistent data were received + """ + super()._process_feedback_data(data) + if self.provider != 'buckaroo': + return + + normalized_data = _normalize_dataset(data) + transaction_keys = normalized_data.get('BRQ_TRANSACTIONS') + if not transaction_keys: + raise ValidationError("Buckaroo: " + _("Received data with missing transaction keys")) + # BRQ_TRANSACTIONS can hold multiple, comma-separated, tx keys. In practice, it holds only + # one reference. So we split for semantic correctness and keep the first transaction key. + self.acquirer_reference = transaction_keys.split(',')[0] + + status_code = int(normalized_data.get('BRQ_STATUSCODE') or 0) + if status_code in self._pending_tx_status: + self._set_pending() + elif status_code in self._valid_tx_status: + self._set_done() + elif status_code in self._cancel_tx_status: + self._set_canceled() + else: + _logger.warning("Buckaroo: received unknown status code: %s", status_code) + self._set_error("Buckaroo: " + _("Unknown status code: %s", status_code)) diff --git a/addons/payment_buckaroo/tests/__init__.py b/addons/payment_buckaroo/tests/__init__.py index 22dc419fcfe..9cf43f8545e 100644 --- a/addons/payment_buckaroo/tests/__init__.py +++ b/addons/payment_buckaroo/tests/__init__.py @@ -1,3 +1,4 @@ -# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import common from . import test_buckaroo diff --git a/addons/payment_buckaroo/tests/common.py b/addons/payment_buckaroo/tests/common.py new file mode 100644 index 00000000000..39e66fa7622 --- /dev/null +++ b/addons/payment_buckaroo/tests/common.py @@ -0,0 +1,19 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.payment.tests.common import PaymentCommon + + +class BuckarooCommon(PaymentCommon): + + @classmethod + def setUpClass(cls): + super().setUpClass() + + cls.buckaroo = cls._prepare_acquirer('buckaroo', update_values={ + 'buckaroo_website_key': 'dummy', + 'buckaroo_secret_key': 'dummy', + }) + + # Override defaults + cls.acquirer = cls.buckaroo + cls.currency = cls.currency_euro diff --git a/addons/payment_buckaroo/tests/test_buckaroo.py b/addons/payment_buckaroo/tests/test_buckaroo.py index c516b7796a4..32b05113948 100644 --- a/addons/payment_buckaroo/tests/test_buckaroo.py +++ b/addons/payment_buckaroo/tests/test_buckaroo.py @@ -1,120 +1,51 @@ -# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. -from lxml import objectify -from werkzeug import urls - -import odoo -from odoo.addons.payment.models.payment_acquirer import ValidationError -from odoo.addons.payment.tests.common import PaymentAcquirerCommon -from odoo.addons.payment_buckaroo.controllers.main import BuckarooController +from odoo.exceptions import ValidationError +from odoo.tests import tagged from odoo.tools import mute_logger +from .common import BuckarooCommon +from ..controllers.main import BuckarooController -@odoo.tests.tagged('post_install', '-at_install', 'external', '-standard') -class BuckarooCommon(PaymentAcquirerCommon): - @classmethod - def setUpClass(cls, chart_template_ref=None): - super().setUpClass(chart_template_ref=chart_template_ref) - # get the buckaroo account - cls.buckaroo = cls.env.ref('payment.payment_acquirer_buckaroo') - cls.buckaroo.write({ - 'brq_websitekey': 'dummy', - 'brq_secretkey': 'dummy', - 'state': 'test', - }) +@tagged('post_install', '-at_install') +class BuckarooTest(BuckarooCommon): -@odoo.tests.tagged('post_install', '-at_install', 'external', '-standard') -class BuckarooForm(BuckarooCommon): - - def test_10_Buckaroo_form_render(self): - base_url = self.env['ir.config_parameter'].get_param('web.base.url') - # be sure not to do stupid things - self.assertEqual(self.buckaroo.state, 'test', 'test without test environment') - - # ---------------------------------------- - # Test: button direct rendering - # ---------------------------------------- - - form_values = { - 'add_returndata': '/payment/process', - 'Brq_websitekey': self.buckaroo.brq_websitekey, - 'Brq_amount': '2240.0', - 'Brq_currency': 'EUR', - 'Brq_invoicenumber': 'SO004', - 'Brq_signature': 'f09bb79451a7ddef3178462ea52b07e55d34e99e', # update me - 'brq_test': 'True', - 'Brq_return': urls.url_join(base_url, BuckarooController._return_url), - 'Brq_returncancel': urls.url_join(base_url, BuckarooController._cancel_url), - 'Brq_returnerror': urls.url_join(base_url, BuckarooController._exception_url), - 'Brq_returnreject': urls.url_join(base_url, BuckarooController._reject_url), + def test_redirect_form_values(self): + return_url = self._build_url(BuckarooController._return_url) + expected_values = { + 'Brq_websitekey': self.buckaroo.buckaroo_website_key, + 'Brq_amount': str(self.amount), + 'Brq_currency': self.currency.name, + 'Brq_invoicenumber': self.reference, + 'Brq_signature': '04c26578116990496770687a8bf225200e0f56e6', + 'Brq_return': return_url, + 'Brq_returncancel': return_url, + 'Brq_returnerror': return_url, + 'Brq_returnreject': return_url, 'Brq_culture': 'en-US', } - # render the button - res = self.buckaroo.render( - 'SO004', 2240.0, self.currency_euro.id, - partner_id=None, - values=self.buyer_values) - # check form result - tree = objectify.fromstring(res) - data_set = tree.xpath("//input[@name='data_set']") - self.assertEqual(len(data_set), 1, 'paypal: Found %d "data_set" input instead of 1' % len(data_set)) - self.assertEqual(data_set[0].get('data-action-url'), 'https://testcheckout.buckaroo.nl/html/', 'Buckaroo: wrong form POST url') - for form_input in tree.input: - if form_input.get('name') in ['submit', 'data_set']: - continue - self.assertEqual( - form_input.get('value'), - form_values[form_input.get('name')], - 'Buckaroo: wrong value for input %s: received %s instead of %s' % (form_input.get('name'), form_input.get('value'), form_values[form_input.get('name')]) - ) + tx_sudo = self.create_transaction(flow='redirect') + with mute_logger('odoo.addons.payment.models.payment_transaction'): + processing_values = tx_sudo._get_processing_values() + form_info = self._extract_values_from_html_form(processing_values['redirect_form_html']) - # ---------------------------------------- - # Test2: button using tx + validation - # ---------------------------------------- + self.assertEqual(form_info['action'], "https://testcheckout.buckaroo.nl/html/") + self.assertDictEqual(expected_values, form_info['inputs'], + "Buckaroo: invalid inputs specified in the redirect form.") - # create a new draft tx - tx = self.env['payment.transaction'].create({ - 'amount': 2240.0, - 'acquirer_id': self.buckaroo.id, - 'currency_id': self.currency_euro.id, - 'reference': 'SO004', - 'partner_id': self.buyer_id, - }) - - # render the button - res = self.buckaroo.render( - 'should_be_erased', 2240.0, self.currency_euro, - partner_id=None, - values=self.buyer_values) - - # check form result - tree = objectify.fromstring(res) - data_set = tree.xpath("//input[@name='data_set']") - self.assertEqual(len(data_set), 1, 'paypal: Found %d "data_set" input instead of 1' % len(data_set)) - self.assertEqual(data_set[0].get('data-action-url'), 'https://testcheckout.buckaroo.nl/html/', 'Buckaroo: wrong form POST url') - for form_input in tree.input: - if form_input.get('name') in ['submit', 'data_set']: - continue - self.assertEqual( - form_input.get('value'), - form_values[form_input.get('name')], - 'Buckaroo: 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')]) - ) - - @mute_logger('odoo.addons.payment_buckaroo.models.payment', 'ValidationError') - def test_20_buckaroo_form_management(self): - # be sure not to do stupid thing - self.assertEqual(self.buckaroo.state, 'test', 'test without test environment') + def test_feedback_processing(self): + self.amount = 2240.0 + self.reference = 'SO004' # typical data posted by buckaroo after client has successfully paid buckaroo_post_data = { 'BRQ_RETURNDATA': u'', - 'BRQ_AMOUNT': u'2240.00', - 'BRQ_CURRENCY': u'EUR', + 'BRQ_AMOUNT': str(self.amount), + 'BRQ_CURRENCY': self.currency.name, 'BRQ_CUSTOMER_NAME': u'Jan de Tester', - 'BRQ_INVOICENUMBER': u'SO004', + 'BRQ_INVOICENUMBER': self.reference, 'brq_payment': u'573311D081B04069BD6336001611DBD4', 'BRQ_PAYMENT_METHOD': u'paypal', 'BRQ_SERVICE_PAYPAL_PAYERCOUNTRY': u'NL', @@ -123,52 +54,43 @@ class BuckarooForm(BuckarooCommon): 'BRQ_SERVICE_PAYPAL_PAYERLASTNAME': u'Tester', 'BRQ_SERVICE_PAYPAL_PAYERMIDDLENAME': u'de', 'BRQ_SERVICE_PAYPAL_PAYERSTATUS': u'verified', - 'Brq_signature': u'9e75e51cbcb6fbc798b40d1787cc5b7c36557d56', + 'Brq_signature': u'e439f3af06b9752197631715628d6a198a25900f', 'BRQ_STATUSCODE': u'190', 'BRQ_STATUSCODE_DETAIL': u'S001', 'BRQ_STATUSMESSAGE': u'Transaction successfully processed', - 'BRQ_TEST': u'true', 'BRQ_TIMESTAMP': u'2014-05-08 12:41:21', 'BRQ_TRANSACTIONS': u'D6106678E1D54EEB8093F5B3AC42EA7B', 'BRQ_WEBSITEKEY': u'5xTGyGyPyl', } - # should raise error about unknown tx - with self.assertRaises(ValidationError): - self.env['payment.transaction'].form_feedback(buckaroo_post_data, 'buckaroo') + with self.assertRaises(ValidationError): # unknown transaction + self.env['payment.transaction']._handle_feedback_data('buckaroo', buckaroo_post_data) - tx = self.env['payment.transaction'].create({ - 'amount': 2240.0, - 'acquirer_id': self.buckaroo.id, - 'currency_id': self.currency_euro.id, - 'reference': 'SO004', - 'partner_name': 'Norbert Buyer', - 'partner_country_id': self.country_france.id}) + tx = self.create_transaction(flow='redirect') # validate it - tx.form_feedback(buckaroo_post_data, 'buckaroo') - # check state + tx._handle_feedback_data('buckaroo', buckaroo_post_data) self.assertEqual(tx.state, 'done', 'Buckaroo: validation did not put tx into done state') - self.assertEqual(tx.acquirer_reference, buckaroo_post_data.get('BRQ_TRANSACTIONS'), 'Buckaroo: validation did not update tx payid') + self.assertEqual(tx.acquirer_reference, buckaroo_post_data.get('BRQ_TRANSACTIONS'), + 'Buckaroo: validation did not update tx payid') - # reset tx - tx = self.env['payment.transaction'].create({ - 'amount': 2240.0, - 'acquirer_id': self.buckaroo.id, - 'currency_id': self.currency_euro.id, - 'reference': 'SO004-2', - 'partner_name': 'Norbert Buyer', - 'partner_country_id': self.country_france.id}) + # New reference for new tx + self.reference = 'SO004-2' + tx = self.create_transaction(flow='redirect') - buckaroo_post_data['BRQ_INVOICENUMBER'] = 'SO004-2' + buckaroo_post_data['BRQ_INVOICENUMBER'] = self.reference # now buckaroo post is ok: try to modify the SHASIGN buckaroo_post_data['Brq_signature'] = '54d928810e343acf5fb0c3ee75fd747ff159ef7a' with self.assertRaises(ValidationError): - tx.form_feedback(buckaroo_post_data, 'buckaroo') + self.env['payment.transaction']._handle_feedback_data('buckaroo', buckaroo_post_data) + # simulate an error buckaroo_post_data['BRQ_STATUSCODE'] = '2' - buckaroo_post_data['Brq_signature'] = '9138e2bf09a708a4eb485ce7777e5406898b689d' - tx.form_feedback(buckaroo_post_data, 'buckaroo') + buckaroo_post_data['Brq_signature'] = 'b8e54e26b2b5a5e697b8ed5085329ea712fd48b2' - # check state - self.assertEqual(tx.state, 'cancel', 'Buckaroo: erroneous validation did not put tx into error state') + # Avoid warning log bc of unknown status code + with mute_logger('odoo.addons.payment_buckaroo.models.payment_transaction'): + self.env['payment.transaction']._handle_feedback_data('buckaroo', buckaroo_post_data) + + self.assertEqual(tx.state, 'error', + 'Buckaroo: unexpected status code should put tx in error state') diff --git a/addons/payment_buckaroo/views/payment_buckaroo_templates.xml b/addons/payment_buckaroo/views/payment_buckaroo_templates.xml index 7b0749923c9..1b6f5c22ca8 100644 --- a/addons/payment_buckaroo/views/payment_buckaroo_templates.xml +++ b/addons/payment_buckaroo/views/payment_buckaroo_templates.xml @@ -1,27 +1,20 @@ - - - + + + diff --git a/addons/payment_buckaroo/views/payment_views.xml b/addons/payment_buckaroo/views/payment_views.xml index 0bc66e52760..4f4638fce88 100644 --- a/addons/payment_buckaroo/views/payment_views.xml +++ b/addons/payment_buckaroo/views/payment_views.xml @@ -1,20 +1,18 @@ - - - acquirer.form.buckaroo - payment.acquirer - - - - - - - - - - + + Buckaroo Acquirer Form + payment.acquirer + + + + + + + + + + -