From b024dd76d4d457eb7debdd896866bf91e2674bc9 Mon Sep 17 00:00:00 2001 From: "Valeriya(vchu)" Date: Thu, 2 Nov 2023 14:33:54 +0100 Subject: [PATCH] [FIX] payment_adyen: add 'amount' to adyen checkout configuration Steps to reproduce: 1. Create a company with currency as Euros. 2. Set up Adyen on that company with the test credentials and enable paypal. 3. Create an invoice with the currency in Euros. 4. Generate a payment link. 5. Attempt to pay on that payment link with paypal. After this commit the Adyen checkout configuration is correctly set up which allows to pay with PayPal opw-3543970 Part-of: odoo/odoo#140711 --- addons/payment_adyen/controllers/main.py | 13 +++------ .../payment_adyen/models/payment_provider.py | 27 +++++++++++++++++-- .../static/src/js/payment_form.js | 10 +++---- .../views/payment_form_templates.xml | 2 +- 4 files changed, 33 insertions(+), 19 deletions(-) diff --git a/addons/payment_adyen/controllers/main.py b/addons/payment_adyen/controllers/main.py index ddc4f008474..10e63b29de4 100644 --- a/addons/payment_adyen/controllers/main.py +++ b/addons/payment_adyen/controllers/main.py @@ -16,7 +16,6 @@ from odoo.http import request from odoo.addons.payment import utils as payment_utils from odoo.addons.payment_adyen import utils as adyen_utils -from odoo.addons.payment_adyen.const import CURRENCY_DECIMALS _logger = logging.getLogger(__name__) @@ -26,22 +25,16 @@ class AdyenController(http.Controller): _webhook_url = '/payment/adyen/notification' @http.route('/payment/adyen/payment_methods', type='json', auth='public') - def adyen_payment_methods(self, provider_id, amount=None, currency_id=None, partner_id=None): + def adyen_payment_methods(self, provider_id, formatted_amount=None, partner_id=None): """ Query the available payment methods based on the payment context. :param int provider_id: The provider handling the transaction, as a `payment.provider` id - :param float amount: The transaction amount - :param int currency_id: The transaction currency, as a `res.currency` id + :param dict formatted_amount: The Adyen-formatted amount. :param int partner_id: The partner making the transaction, as a `res.partner` id :return: The JSON-formatted content of the response :rtype: dict """ provider_sudo = request.env['payment.provider'].sudo().browse(provider_id) - currency = request.env['res.currency'].browse(currency_id) - currency_code = currency_id and currency.name - converted_amount = amount and currency_code and payment_utils.to_minor_currency_units( - amount, currency, CURRENCY_DECIMALS.get(currency_code) - ) partner_sudo = partner_id and request.env['res.partner'].sudo().browse(partner_id).exists() # The lang is taken from the context rather than from the partner because it is not required # to be logged in to make a payment, and because the lang is not always set on the partner. @@ -52,7 +45,7 @@ class AdyenController(http.Controller): shopper_reference = partner_sudo and f'ODOO_PARTNER_{partner_sudo.id}' data = { 'merchantAccount': provider_sudo.adyen_merchant_account, - 'amount': converted_amount, + 'amount': formatted_amount, 'countryCode': partner_sudo.country_id.code or None, # ISO 3166-1 alpha-2 (e.g.: 'BE') 'shopperLocale': lang_code, # IETF language tag (e.g.: 'fr-BE') 'shopperReference': shopper_reference, diff --git a/addons/payment_adyen/models/payment_provider.py b/addons/payment_adyen/models/payment_provider.py index b788b8509d5..53665ab9280 100644 --- a/addons/payment_adyen/models/payment_provider.py +++ b/addons/payment_adyen/models/payment_provider.py @@ -1,13 +1,14 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. +import json import logging import re -import json import requests from odoo import _, api, fields, models from odoo.exceptions import ValidationError +from odoo.addons.payment import utils as payment_utils from odoo.addons.payment_adyen import const _logger = logging.getLogger(__name__) @@ -144,12 +145,14 @@ class PaymentProvider(models.Model): #=== BUSINESS METHODS - GETTERS ===# - def _adyen_get_inline_form_values(self, pm_code): + def _adyen_get_inline_form_values(self, pm_code, amount=None, currency=None): """ Return a serialized JSON of the required values to render the inline form. Note: `self.ensure_one()` :param str pm_code: The code of the payment method whose inline form to render. + :param float amount: The transaction amount. + :param res.currency currency: The transaction currency. :return: The JSON serial of the required values to render the inline form. :rtype: str """ @@ -158,5 +161,25 @@ class PaymentProvider(models.Model): inline_form_values = { 'client_key': self.adyen_client_key, 'adyen_pm_code': const.PAYMENT_METHODS_MAPPING.get(pm_code, pm_code), + 'formatted_amount': self._adyen_get_formatted_amount(amount, currency), } return json.dumps(inline_form_values) + + def _adyen_get_formatted_amount(self, amount=None, currency=None): + """ Return the amount in the format required by Adyen. + + The formatted amount is a dict with keys 'value' and 'currency'. + + :param float amount: The transaction amount. + :param res.currency currency: The transaction currency. + :return: The Adyen-formatted amount. + :rtype: dict + """ + currency_code = currency and currency.name + converted_amount = amount and currency_code and payment_utils.to_minor_currency_units( + amount, currency, const.CURRENCY_DECIMALS.get(currency_code) + ) + return { + 'value': converted_amount, + 'currency': currency_code, + } diff --git a/addons/payment_adyen/static/src/js/payment_form.js b/addons/payment_adyen/static/src/js/payment_form.js index 2fd502da0ef..58f11d3c354 100644 --- a/addons/payment_adyen/static/src/js/payment_form.js +++ b/addons/payment_adyen/static/src/js/payment_form.js @@ -48,24 +48,22 @@ paymentForm.include({ // Extract and deserialize the inline form values. const radio = document.querySelector('input[name="o_payment_radio"]:checked'); const inlineFormValues = JSON.parse(radio.dataset['inlineFormValues']); + const formatted_amount = inlineFormValues['formatted_amount']; + const formattedAmount = inlineFormValues['formatted_amount']; // Create the checkout object if not already done for another payment method. if (!this.adyenCheckout) { await this.rpc('/payment/adyen/payment_methods', { // Await the RPC to let it create AdyenCheckout before using it. 'provider_id': providerId, 'partner_id': parseInt(this.paymentContext['partnerId']), - 'amount': this.paymentContext['amount'] - ? parseFloat(this.paymentContext['amount']) - : undefined, - 'currency_id': this.paymentContext['currencyId'] - ? parseInt(this.paymentContext['currencyId']) - : undefined, + 'formatted_amount': formattedAmount, }).then(async response => { // Create the Adyen Checkout SDK. const providerState = this._getProviderState(radio); const configuration = { paymentMethodsResponse: response, clientKey: inlineFormValues['client_key'], + amount: formattedAmount, locale: (this._getContext().lang || 'en-US').replace('_', '-'), environment: providerState === 'enabled' ? 'live' : 'test', onAdditionalDetails: this._adyenOnSubmitAdditionalDetails.bind(this), diff --git a/addons/payment_adyen/views/payment_form_templates.xml b/addons/payment_adyen/views/payment_form_templates.xml index 7d0b6c23f36..474e8af59c2 100644 --- a/addons/payment_adyen/views/payment_form_templates.xml +++ b/addons/payment_adyen/views/payment_form_templates.xml @@ -19,7 +19,7 @@