diff --git a/addons/account/controllers/portal.py b/addons/account/controllers/portal.py index 44355f227f8..6d2aeb3fa58 100644 --- a/addons/account/controllers/portal.py +++ b/addons/account/controllers/portal.py @@ -99,11 +99,6 @@ class PortalAccount(CustomerPortal): return self._show_report(model=invoice_sudo, report_type=report_type, report_ref='account.account_invoices', download=download) values = self._invoice_get_page_view_values(invoice_sudo, access_token, **kw) - acquirers = values.get('acquirers') - if acquirers: - country_id = values.get('partner_id') and values.get('partner_id')[0].country_id.id - values['acq_extra_fees'] = acquirers.get_acquirer_extra_fees(invoice_sudo.amount_residual, invoice_sudo.currency_id, country_id) - return request.render("account.portal_invoice_page", values) # ------------------------------------------------------------ diff --git a/addons/account_payment/__init__.py b/addons/account_payment/__init__.py index 3ea0f0cac45..dd15b5c32e5 100644 --- a/addons/account_payment/__init__.py +++ b/addons/account_payment/__init__.py @@ -2,4 +2,3 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import controllers -from . import models \ No newline at end of file diff --git a/addons/account_payment/controllers/payment.py b/addons/account_payment/controllers/payment.py index 84b90b72c68..23a71f8e840 100644 --- a/addons/account_payment/controllers/payment.py +++ b/addons/account_payment/controllers/payment.py @@ -1,93 +1,38 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from werkzeug.urls import url_encode +from odoo.exceptions import AccessError, MissingError, ValidationError +from odoo.fields import Command +from odoo.http import route -from odoo import http, _ -from odoo.addons.portal.controllers.portal import _build_url_w_params -from odoo.addons.payment.controllers.portal import PaymentProcessing -from odoo.http import request, route +from odoo.addons.payment.controllers import portal as payment_portal -class PaymentPortal(http.Controller): +class PaymentPortal(payment_portal.PaymentPortal): - @route('/invoice/pay//form_tx', type='json', auth="public", website=True) - def invoice_pay_form(self, acquirer_id, invoice_id, save_token=False, access_token=None, **kwargs): - """ Json method that creates a payment.transaction, used to create a - transaction when the user clicks on 'pay now' button on the payment - form. - - :return html: form containing all values related to the acquirer to - redirect customers to the acquirer website """ - invoice_sudo = request.env['account.move'].sudo().browse(invoice_id) - if not invoice_sudo: - return False + @route('/invoice/transaction/', type='json', auth='public') + def invoice_transaction(self, invoice_id, access_token, **kwargs): + """ Create a draft transaction and return its processing values. + :param int invoice_id: The invoice to pay, as an `account.move` id + :param str access_token: The access token used to authenticate the request + :param dict kwargs: Locally unused data passed to `_create_transaction` + :return: The mandatory values for the processing of the transaction + :rtype: dict + :raise: ValidationError if the invoice id or the access token is invalid + """ + # Check the invoice id and the access token try: - acquirer_id = int(acquirer_id) - except: - return False + self._document_check_access('account.move', invoice_id, access_token) + except MissingError as error: + raise error + except AccessError: + raise ValidationError("The access token is invalid.") - if request.env.user._is_public(): - save_token = False # we avoid to create a token for the public user - - success_url = kwargs.get( - 'success_url', "%s?%s" % (invoice_sudo.access_url, url_encode({'access_token': access_token}) if access_token else '') - ) - vals = { - 'acquirer_id': acquirer_id, - 'return_url': success_url, - } - - if save_token: - vals['type'] = 'form_save' - - transaction = invoice_sudo._create_payment_transaction(vals) - PaymentProcessing.add_payment_transaction(transaction) - - return transaction.render_invoice_button( - invoice_sudo, - submit_txt=_('Pay & Confirm'), - render_values={ - 'type': 'form_save' if save_token else 'form', - 'alias_usage': _('If we store your payment information on our server, subscription payments will be made automatically.'), - } + kwargs['reference_prefix'] = None # Allow the reference to be computed based on the invoice + kwargs.pop('custom_create_values', None) # Don't allow passing arbitrary create values + tx_sudo = self._create_transaction( + custom_create_values={'invoice_ids': [Command.set([invoice_id])]}, **kwargs, ) - @http.route('/invoice/pay//s2s_token_tx', type='http', auth='public', website=True) - def invoice_pay_token(self, invoice_id, pm_id=None, **kwargs): - """ Use a token to perform a s2s transaction """ - error_url = kwargs.get('error_url', '/my') - access_token = kwargs.get('access_token') - params = {} - if access_token: - params['access_token'] = access_token - - invoice_sudo = request.env['account.move'].sudo().browse(invoice_id).exists() - if not invoice_sudo: - params['error'] = 'pay_invoice_invalid_doc' - return request.redirect(_build_url_w_params(error_url, params)) - - success_url = kwargs.get( - 'success_url', "%s?%s" % (invoice_sudo.access_url, url_encode({'access_token': access_token}) if access_token else '') - ) - try: - token = request.env['payment.token'].sudo().browse(int(pm_id)) - except (ValueError, TypeError): - token = False - token_owner = invoice_sudo.partner_id if request.env.user._is_public() else request.env.user.partner_id - if not token or token.partner_id != token_owner: - params['error'] = 'pay_invoice_invalid_token' - return request.redirect(_build_url_w_params(error_url, params)) - - vals = { - 'payment_token_id': token.id, - 'type': 'server2server', - 'return_url': _build_url_w_params(success_url, params), - } - - tx = invoice_sudo._create_payment_transaction(vals) - PaymentProcessing.add_payment_transaction(tx) - - params['success'] = 'pay_invoice' - return request.redirect('/payment/process') + return tx_sudo._get_processing_values() diff --git a/addons/account_payment/controllers/portal.py b/addons/account_payment/controllers/portal.py index acf4b57d27a..bfdf2279434 100644 --- a/addons/account_payment/controllers/portal.py +++ b/addons/account_payment/controllers/portal.py @@ -1,26 +1,51 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo.addons.account.controllers import portal from odoo.http import request +from odoo.addons.account.controllers import portal +from odoo.addons.portal.controllers.portal import _build_url_w_params + class PortalAccount(portal.PortalAccount): def _invoice_get_page_view_values(self, invoice, access_token, **kwargs): - values = super(PortalAccount, self)._invoice_get_page_view_values(invoice, access_token, **kwargs) - payment_inputs = request.env['payment.acquirer']._get_available_payment_input(partner=invoice.partner_id, company=invoice.company_id) - # if not connected (using public user), the method _get_available_payment_input will return public user tokens - is_public_user = request.env.user._is_public() - if is_public_user: - # we should not display payment tokens owned by the public user - payment_inputs.pop('pms', None) - token_count = request.env['payment.token'].sudo().search_count([('acquirer_id.company_id', '=', invoice.company_id.id), - ('partner_id', '=', invoice.partner_id.id), - ]) - values['existing_token'] = token_count > 0 - values.update(payment_inputs) - # if the current user is connected we set partner_id to his partner otherwise we set it as the invoice partner - # we do this to force the creation of payment tokens to the correct partner and avoid token linked to the public user - values['partner_id'] = invoice.partner_id if is_public_user else request.env.user.partner_id, + values = super()._invoice_get_page_view_values(invoice, access_token, **kwargs) + logged_in = not request.env.user._is_public() + # We set partner_id to the partner id of the current user if logged in, otherwise we set it + # to the invoice partner id. We do this to ensure that payment tokens are assigned to the + # correct partner and to avoid linking tokens to the public user. + partner_id = request.env.user.partner_id.id if logged_in else invoice.partner_id.id + acquirers_sudo = request.env['payment.acquirer'].sudo()._get_compatible_acquirers( + invoice.company_id.id or request.env.company.id, + partner_id, + currency_id=invoice.currency_id.id, + ) # In sudo mode to read the fields of acquirers and partner (if not logged in) + tokens = request.env['payment.token'].search( + [('acquirer_id', 'in', acquirers_sudo.ids), ('partner_id', '=', partner_id)] + ) # Tokens are cleared at the end if the user is not logged in + fees_by_acquirer = { + acq_sudo: acq_sudo._compute_fees( + invoice.amount_total, invoice.currency_id, invoice.company_id.country_id + ) for acq_sudo in acquirers_sudo.filtered('fees_active') + } + values.update({ + 'acquirers': acquirers_sudo, + 'tokens': tokens, + 'fees_by_acquirer': fees_by_acquirer, + 'show_tokenize_input': logged_in, # Prevent public partner from saving payment methods + 'amount': invoice.amount_residual, + 'currency': invoice.currency_id, + 'partner_id': partner_id, + 'access_token': access_token, + 'transaction_route': f'/invoice/transaction/{invoice.id}/', + 'landing_route': _build_url_w_params(invoice.access_url, {'access_token': access_token}) + }) + if not logged_in: + # Don't display payment tokens of the invoice partner if the user is not logged in, but + # inform that logging in will make them available. + values.update({ + 'existing_token': bool(tokens), + 'tokens': request.env['payment.token'], + }) return values diff --git a/addons/account_payment/models/__init__.py b/addons/account_payment/models/__init__.py deleted file mode 100644 index 2ec5b9cd2f6..00000000000 --- a/addons/account_payment/models/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from . import payment diff --git a/addons/account_payment/models/payment.py b/addons/account_payment/models/payment.py deleted file mode 100644 index decad0d034a..00000000000 --- a/addons/account_payment/models/payment.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -import logging - -from odoo import fields, models, _ -from odoo.tools import float_compare - -_logger = logging.getLogger(__name__) - - -class PaymentTransaction(models.Model): - _inherit = 'payment.transaction' - - def render_invoice_button(self, invoice, submit_txt=None, render_values=None): - values = { - 'partner_id': invoice.partner_id.id, - } - if render_values: - values.update(render_values) - return self.acquirer_id.with_context(submit_class='btn btn-primary', submit_txt=submit_txt or _('Pay Now')).sudo().render( - self.reference, - invoice.amount_residual, - invoice.currency_id.id, - values=values, - ) diff --git a/addons/account_payment/views/account_portal_templates.xml b/addons/account_payment/views/account_portal_templates.xml index 5edfdd044ce..7dede58ee2f 100644 --- a/addons/account_payment/views/account_portal_templates.xml +++ b/addons/account_payment/views/account_portal_templates.xml @@ -6,7 +6,7 @@ - + Pay Now @@ -16,13 +16,13 @@ - + Waiting for Payment Authorized - + Pending @@ -39,7 +39,7 @@