From 3566dc9005d5ddec916ebd0a4765895d2139f801 Mon Sep 17 00:00:00 2001 From: Antoine Prieels Date: Sat, 26 Sep 2020 12:53:29 +0000 Subject: [PATCH] [ADD] payment_odoo_by_adyen: Adyen for Platforms in eCommerce Adyen for Platforms integration in eCommerce. closes odoo/odoo#58752 Taskid: 2129218 Signed-off-by: pimodoo --- addons/payment/data/payment_acquirer_data.xml | 29 +++ addons/payment_odoo_by_adyen/__init__.py | 5 + addons/payment_odoo_by_adyen/__manifest__.py | 19 ++ .../controllers/__init__.py | 4 + .../payment_odoo_by_adyen/controllers/main.py | 22 +++ .../data/payment_acquirer_data.xml | 14 ++ .../payment_odoo_by_adyen/models/__init__.py | 4 + .../payment_odoo_by_adyen/models/payment.py | 179 ++++++++++++++++++ .../static/src/img/odoo_icon.png | Bin 0 -> 12863 bytes .../views/payment_odoo_by_adyen_templates.xml | 9 + .../views/payment_views.xml | 22 +++ 11 files changed, 307 insertions(+) create mode 100644 addons/payment_odoo_by_adyen/__init__.py create mode 100644 addons/payment_odoo_by_adyen/__manifest__.py create mode 100644 addons/payment_odoo_by_adyen/controllers/__init__.py create mode 100644 addons/payment_odoo_by_adyen/controllers/main.py create mode 100644 addons/payment_odoo_by_adyen/data/payment_acquirer_data.xml create mode 100644 addons/payment_odoo_by_adyen/models/__init__.py create mode 100644 addons/payment_odoo_by_adyen/models/payment.py create mode 100644 addons/payment_odoo_by_adyen/static/src/img/odoo_icon.png create mode 100644 addons/payment_odoo_by_adyen/views/payment_odoo_by_adyen_templates.xml create mode 100644 addons/payment_odoo_by_adyen/views/payment_views.xml diff --git a/addons/payment/data/payment_acquirer_data.xml b/addons/payment/data/payment_acquirer_data.xml index 6175435b0c2..63447edcc06 100644 --- a/addons/payment/data/payment_acquirer_data.xml +++ b/addons/payment/data/payment_acquirer_data.xml @@ -12,6 +12,35 @@ + + Odoo Payments by Adyen + Credit Card (powered by Odoo Payments) + + + + 1 + +

+ A payment gateway to accept online payments via credit cards, debit cards and bank transfers. +

+
    +
  • Online Payment
  • +
  • Subscriptions
  • +
  • Save Cards
  • +
  • Payment Status Tracking
  • +
+
+ + +
+ Buckaroo Credit Card (powered by Buckaroo) diff --git a/addons/payment_odoo_by_adyen/__init__.py b/addons/payment_odoo_by_adyen/__init__.py new file mode 100644 index 00000000000..7d34c7c054a --- /dev/null +++ b/addons/payment_odoo_by_adyen/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import controllers +from . import models diff --git a/addons/payment_odoo_by_adyen/__manifest__.py b/addons/payment_odoo_by_adyen/__manifest__.py new file mode 100644 index 00000000000..b3d7b76a9ea --- /dev/null +++ b/addons/payment_odoo_by_adyen/__manifest__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +{ + 'name': 'Odoo Payments by Adyen Payment Acquirer', + 'category': 'Accounting/Payment Acquirers', + 'sequence': 330, + 'summary': 'Payment Acquirer: Odoo Payments by Adyen', + 'version': '1.0', + 'description': """Odoo Payments by Adyen""", + 'depends': ['payment', 'adyen_platforms'], + 'data': [ + 'views/payment_views.xml', + 'views/payment_odoo_by_adyen_templates.xml', + 'data/payment_acquirer_data.xml', + ], + 'installable': True, + 'application': True, +} diff --git a/addons/payment_odoo_by_adyen/controllers/__init__.py b/addons/payment_odoo_by_adyen/controllers/__init__.py new file mode 100644 index 00000000000..5d4b25db9c0 --- /dev/null +++ b/addons/payment_odoo_by_adyen/controllers/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import main diff --git a/addons/payment_odoo_by_adyen/controllers/main.py b/addons/payment_odoo_by_adyen/controllers/main.py new file mode 100644 index 00000000000..cb2182e81b8 --- /dev/null +++ b/addons/payment_odoo_by_adyen/controllers/main.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import json +import logging +import pprint + +from odoo import http +from odoo.http import request + +_logger = logging.getLogger(__name__) + + +class OdooByAdyenController(http.Controller): + _notification_url = '/payment/odoo_adyen/notification' + + @http.route('/payment/odoo_adyen/notification', type='json', auth='public', csrf=False) + def odoo_adyen_notification(self): + data = json.loads(request.httprequest.data) + _logger.info('Beginning Odoo by Adyen form_feedback with data %s', pprint.pformat(data)) + if data.get('authResult') not in ['CANCELLED']: + request.env['payment.transaction'].sudo().form_feedback(data, 'odoo_adyen') diff --git a/addons/payment_odoo_by_adyen/data/payment_acquirer_data.xml b/addons/payment_odoo_by_adyen/data/payment_acquirer_data.xml new file mode 100644 index 00000000000..f6572c102c6 --- /dev/null +++ b/addons/payment_odoo_by_adyen/data/payment_acquirer_data.xml @@ -0,0 +1,14 @@ + + + + + + Odoo Payments by Adyen + + odoo_adyen + + + + + + diff --git a/addons/payment_odoo_by_adyen/models/__init__.py b/addons/payment_odoo_by_adyen/models/__init__.py new file mode 100644 index 00000000000..2ec5b9cd2f6 --- /dev/null +++ b/addons/payment_odoo_by_adyen/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import payment diff --git a/addons/payment_odoo_by_adyen/models/payment.py b/addons/payment_odoo_by_adyen/models/payment.py new file mode 100644 index 00000000000..9e7a4dc2afa --- /dev/null +++ b/addons/payment_odoo_by_adyen/models/payment.py @@ -0,0 +1,179 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import hashlib +import hmac +import json +import logging +from werkzeug import urls + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError +from odoo.addons.payment_odoo_by_adyen.controllers.main import OdooByAdyenController + +_logger = logging.getLogger(__name__) + + +class AcquirerOdooByAdyen(models.Model): + _inherit = 'payment.acquirer' + + provider = fields.Selection(selection_add=[ + ('odoo_adyen', 'Odoo Payments by Adyen') + ], ondelete={'odoo_adyen': 'set default'}) + odoo_adyen_account_id = fields.Many2one('adyen.account', required_if_provider='odoo_adyen', related='company_id.adyen_account_id') + odoo_adyen_payout_id = fields.Many2one('adyen.payout', required_if_provider='odoo_adyen', string='Adyen Payout', domain="[('adyen_account_id', '=', odoo_adyen_account_id)]") + + def _get_feature_support(self): + res = super(AcquirerOdooByAdyen, self)._get_feature_support() + res['tokenize'].append('odoo_adyen') + return res + + @api.model + def _odoo_adyen_format_amount(self, amount, currency_id): + return { + 'value': int(amount * (10 ** currency_id.decimal_places)), + 'currency': currency_id.name, + } + + @api.model + def _odoo_adyen_compute_signature(self, amount, currency_id, reference): + secret = self.env['ir.config_parameter'].sudo().get_param('database.secret') + token_str = '%s%s%s' % ( + int(amount * (10 ** currency_id.decimal_places)), + currency_id.name, + reference + ) + return hmac.new(secret.encode('utf-8'), token_str.encode('utf-8'), hashlib.sha256).hexdigest() + + def odoo_adyen_form_generate_values(self, values): + # Don't use the value returned by `self.get_base_url` for the notification_url as + # `request.httprequest.url_root` could be forged to retrieve the signature and + # fake a payment update + base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + data = { + 'adyen_uuid': self.odoo_adyen_account_id.adyen_uuid, + 'payout': self.odoo_adyen_payout_id.code, + 'amount': self._odoo_adyen_format_amount(values['amount'], values['currency']), + 'reference': values['reference'], + 'shopperLocale': values.get('partner_lang'), + 'metadata': { + 'merchant_signature': self._odoo_adyen_compute_signature(values['amount'],values['currency'],values['reference']), + 'notification_url': urls.url_join(base_url, OdooByAdyenController._notification_url), + }, + 'returnUrl': urls.url_join(self.get_base_url(), '/payment/process'), + } + + if self.save_token in ['ask', 'always']: + data.update({ + 'shopperReference': '%s_%s' % (self.odoo_adyen_account_id.adyen_uuid, values['partner_id']), + 'storePaymentMethod': True, + 'recurringProcessingModel': 'CardOnFile', + }) + + values.update({ + 'data': json.dumps(data), + }) + return values + + def odoo_adyen_get_form_action_url(self): + self.ensure_one() + proxy_url = self.env['ir.config_parameter'].sudo().get_param('adyen_platforms.proxy_url') + return urls.url_join(proxy_url, 'pay_by_link') + + def odoo_adyen_create_account(self): + return self.env['adyen.account'].action_create_redirect() + +class TxOdooByAdyen(models.Model): + _inherit = 'payment.transaction' + + def odoo_adyen_s2s_do_transaction(self, **kwargs): + self.ensure_one() + # Don't use the value returned by `self.get_base_url` for the notification_url as + # `request.httprequest.url_root` could be forged to retrieve the signature and + # fake a payment update + base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + data = { + 'payout': self.acquirer_id.odoo_adyen_payout_id.code, + 'amount': self.acquirer_id._odoo_adyen_format_amount(self.amount, self.currency_id), + 'reference': self.reference, + 'paymentMethod': { + 'type': self.payment_token_id.odoo_adyen_payment_method_type, + 'storedPaymentMethodId': self.payment_token_id.acquirer_ref, + }, + 'shopperReference': '%s_%s' % (self.acquirer_id.odoo_adyen_account_id.adyen_uuid, self.partner_id.id), + 'shopperInteraction': 'ContAuth', + 'metadata': { + 'merchant_signature': self.acquirer_id._odoo_adyen_compute_signature(self.amount, self.currency_id, self.reference), + 'notification_url': urls.url_join(base_url, OdooByAdyenController._notification_url), + }, + 'returnUrl': urls.url_join(self.get_base_url(), '/payment/process'), + } + self.acquirer_id.odoo_adyen_account_id._adyen_rpc('payments', data) + + @api.model + def _odoo_adyen_form_get_tx_from_data(self, data): + reference = data.get('merchantReference') + if not reference: + error_msg = _('Odoo Payments by Adyen: received data with missing reference (%s)', reference) + _logger.info(error_msg) + raise ValidationError(error_msg) + + tx = self.env['payment.transaction'].search([('reference', '=', reference)]) + if not tx or len(tx) > 1: + error_msg = _('Odoo Payments by Adyen: 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) + + return tx + + def _odoo_adyen_form_get_invalid_parameters(self, data): + invalid_parameters = [] + + if self.acquirer_reference and data.get('pspReference') != self.acquirer_reference: + invalid_parameters.append(('pspReference', data.get('pspReference'), self.acquirer_reference)) + + return invalid_parameters + + def _odoo_adyen_form_validate(self, data): + merchant_signature = self.acquirer_id._odoo_adyen_compute_signature(self.amount, self.currency_id, self.reference) + if merchant_signature != data['additionalData']['metadata.merchant_signature']: + return False + + # Save token + if self.partner_id and not self.payment_token_id and \ + (self.type == 'form_save' or self.acquirer_id.save_token == 'always') \ + and 'recurring.shopperReference' in data['additionalData']: + res = self.acquirer_id.odoo_adyen_account_id._adyen_rpc('payment_methods', { + 'shopperReference': data['additionalData']['recurring.shopperReference'] + }) + stored_payment_methods = res['storedPaymentMethods'] + pm_id = data['additionalData']['recurring.recurringDetailReference'] + token_id = self.env['payment.token'].create({ + 'name': _("Card No XXXXXXXXXXXX%s", data['additionalData']['cardSummary']), + 'acquirer_ref': pm_id, + 'acquirer_id': self.acquirer_id.id, + 'partner_id': self.partner_id.id, + 'odoo_adyen_payment_method_type': next(pm['type'] for pm in stored_payment_methods if pm['id'] == pm_id) + }) + self.payment_token_id = token_id + + # Update status + if data['success']: + self.write({'acquirer_reference': data.get('pspReference')}) + self._set_transaction_done() + return True + else: + error = _('Odoo Payment by Adyen: feedback error') + _logger.info(error) + self.write({'state_message': error}) + self._set_transaction_cancel() + return False + +class PaymentToken(models.Model): + _inherit = 'payment.token' + + odoo_adyen_payment_method_type = fields.Char(string='PaymentMethod Type') diff --git a/addons/payment_odoo_by_adyen/static/src/img/odoo_icon.png b/addons/payment_odoo_by_adyen/static/src/img/odoo_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5701852c578844abf0fff2d2c0dc36fd5d0595fc GIT binary patch literal 12863 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 qFO0%+uJggi-4>+U&vM+H`=h + + + + + diff --git a/addons/payment_odoo_by_adyen/views/payment_views.xml b/addons/payment_odoo_by_adyen/views/payment_views.xml new file mode 100644 index 00000000000..715aefac291 --- /dev/null +++ b/addons/payment_odoo_by_adyen/views/payment_views.xml @@ -0,0 +1,22 @@ + + + + + + acquirer.form.odoo.by.adyen + payment.acquirer + + + + +