diff --git a/addons/payment_adyen/__manifest__.py b/addons/payment_adyen/__manifest__.py
index d298bf8916f..cd8449513f6 100644
--- a/addons/payment_adyen/__manifest__.py
+++ b/addons/payment_adyen/__manifest__.py
@@ -17,9 +17,10 @@
'uninstall_hook': 'uninstall_hook',
'assets': {
'web.assets_frontend': [
- 'https://checkoutshopper-live.adyen.com/checkoutshopper/sdk/3.9.4/adyen.css',
- 'https://checkoutshopper-live.adyen.com/checkoutshopper/sdk/3.9.4/adyen.js',
+ 'https://checkoutshopper-live.adyen.com/checkoutshopper/sdk/4.7.3/adyen.css',
+ 'https://checkoutshopper-live.adyen.com/checkoutshopper/sdk/4.7.3/adyen.js',
'payment_adyen/static/src/js/payment_form.js',
+ 'payment_adyen/static/src/scss/dropin.scss',
],
},
'license': 'LGPL-3',
diff --git a/addons/payment_adyen/const.py b/addons/payment_adyen/const.py
index 83325654431..9cac03fb2e6 100644
--- a/addons/payment_adyen/const.py
+++ b/addons/payment_adyen/const.py
@@ -1,15 +1,14 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
# Endpoints of the API.
-# See https://docs.adyen.com/api-explorer/#/CheckoutService/v53/overview for Checkout API
+# See https://docs.adyen.com/api-explorer/#/CheckoutService/v67/overview for Checkout API
# See https://docs.adyen.com/api-explorer/#/Recurring/v49/overview for Recurring API
API_ENDPOINT_VERSIONS = {
'/disable': 49, # Recurring API
- '/originKeys': 53, # Checkout API
- '/payments': 53, # Checkout API
- '/payments/details': 53, # Checkout API
+ '/payments': 67, # Checkout API
+ '/payments/details': 67, # Checkout API
'/payments/{}/refunds': 67, # Checkout API
- '/paymentMethods': 53, # Checkout API
+ '/paymentMethods': 67, # Checkout API
}
# Adyen-specific mapping of currency codes in ISO 4217 format to the number of decimals.
diff --git a/addons/payment_adyen/controllers/main.py b/addons/payment_adyen/controllers/main.py
index 85511e1e8dc..1dadf17c480 100644
--- a/addons/payment_adyen/controllers/main.py
+++ b/addons/payment_adyen/controllers/main.py
@@ -24,16 +24,19 @@ _logger = logging.getLogger(__name__)
class AdyenController(http.Controller):
- @http.route('/payment/adyen/acquirer_state', type='json', auth='public')
- def adyen_acquirer_state(self, acquirer_id):
- """ Return the state of the acquirer.
+ @http.route('/payment/adyen/acquirer_info', type='json', auth='public')
+ def adyen_acquirer_info(self, acquirer_id):
+ """ Return public information on the acquirer.
:param int acquirer_id: The acquirer handling the transaction, as a `payment.acquirer` id
- :return: The state of the acquirer: 'enabled' or 'test'
+ :return: Public information on the acquirer, namely: the state and client key
:rtype: str
"""
acquirer_sudo = request.env['payment.acquirer'].sudo().browse(acquirer_id).exists()
- return acquirer_sudo.state
+ return {
+ 'state': acquirer_sudo.state,
+ 'client_key': acquirer_sudo.adyen_client_key,
+ }
@http.route('/payment/adyen/payment_methods', type='json', auth='public')
def adyen_payment_methods(self, acquirer_id, amount=None, currency_id=None, partner_id=None):
@@ -77,34 +80,12 @@ class AdyenController(http.Controller):
_logger.info("paymentMethods request response:\n%s", pprint.pformat(response_content))
return response_content
- @http.route('/payment/adyen/origin_key', type='json', auth='public')
- def adyen_origin_key(self, acquirer_id):
- """ Request an origin key based on the current domain.
-
- :param int acquirer_id: The acquirer handling the transaction, as a `payment.acquirer` id
- :return: The JSON-formatted content of the response
- :rtype: dict
- """
- acquirer_sudo = request.env['payment.acquirer'].browse(acquirer_id).sudo()
- domain = acquirer_sudo.get_base_url()
- data = {
- 'originDomains': [domain],
- }
- response_content = acquirer_sudo._adyen_make_request(
- url_field_name='adyen_checkout_api_url',
- endpoint='/originKeys',
- payload=data,
- method='POST'
- )
- _logger.info("originKeys request response:\n%s", pprint.pformat(response_content))
- return response_content
-
@http.route('/payment/adyen/payments', type='json', auth='public')
def adyen_payments(
self, acquirer_id, reference, converted_amount, currency_id, partner_id, payment_method,
access_token, browser_info=None
):
- """ Make a payment request and handle the response.
+ """ Make a payment request and process the feedback data.
:param int acquirer_id: The acquirer handling the transaction, as a `payment.acquirer` id
:param str reference: The reference of the transaction
@@ -149,7 +130,7 @@ class AdyenController(http.Controller):
'returnUrl': urls.url_join(
acquirer_sudo.get_base_url(),
# Include the reference in the return url to be able to match it after redirection.
- # The key 'merchantReference' is chosen on purpose to be the same than that returned
+ # The key 'merchantReference' is chosen on purpose to be the same as that returned
# by the /payments endpoint of Adyen.
f'/payment/adyen/return?merchantReference={reference}'
),
@@ -166,35 +147,27 @@ class AdyenController(http.Controller):
request.env['payment.transaction'].sudo()._handle_feedback_data(
'adyen', dict(response_content, merchantReference=reference), # Match the transaction
)
- if 'action' in response_content and response_content['action']['type'] == 'redirect':
- tx_sudo.adyen_payment_data = response_content['paymentData']
-
return response_content
@http.route('/payment/adyen/payment_details', type='json', auth='public')
- def adyen_payment_details(self, acquirer_id, reference, details, payment_data):
- """ Query the status of a transaction that required additional actions and process it.
+ def adyen_payment_details(self, acquirer_id, reference, payment_details):
+ """ Submit the details of the additional actions and process the feedback data.
The additional actions can have been performed both from the inline form or during a
redirection.
:param int acquirer_id: The acquirer handling the transaction, as a `payment.acquirer` id
:param str reference: The reference of the transaction
- :param dict details: The specification of the additional actions
- :param str payment_data: The encrypted payment data of the transaction
+ :param dict payment_details: The details of the additional actions performed for the payment
:return: The JSON-formatted content of the response
:rtype: dict
"""
# Make the payment details request to Adyen
acquirer_sudo = request.env['payment.acquirer'].browse(acquirer_id).sudo()
- data = {
- 'details': details,
- 'paymentData': payment_data,
- }
response_content = acquirer_sudo._adyen_make_request(
url_field_name='adyen_checkout_api_url',
endpoint='/payments/details',
- payload=data,
+ payload=payment_details,
method='POST'
)
@@ -237,8 +210,11 @@ class AdyenController(http.Controller):
self.adyen_payment_details(
tx_sudo.acquirer_id.id,
data['merchantReference'],
- {detail: value for detail, value in data.items() if detail != 'merchantReference'},
- tx_sudo.adyen_payment_data,
+ {
+ 'details': {
+ 'redirectResult': data['redirectResult'],
+ },
+ },
)
# Redirect the user to the status page
diff --git a/addons/payment_adyen/models/payment_acquirer.py b/addons/payment_adyen/models/payment_acquirer.py
index 329dd4f82eb..4c6aba0402b 100644
--- a/addons/payment_adyen/models/payment_acquirer.py
+++ b/addons/payment_adyen/models/payment_acquirer.py
@@ -23,8 +23,11 @@ class PaymentAcquirer(models.Model):
help="The code of the merchant account to use with this acquirer",
required_if_provider='adyen', groups='base.group_system')
adyen_api_key = fields.Char(
- string="API Key", help="The API key of the user account", required_if_provider='adyen',
+ string="API Key", help="The API key of the webservice user", required_if_provider='adyen',
groups='base.group_system')
+ adyen_client_key = fields.Char(
+ string="Client Key", help="The client key of the webservice user",
+ required_if_provider='adyen')
adyen_hmac_key = fields.Char(
string="HMAC Key", help="The HMAC key of the webhook", required_if_provider='adyen',
groups='base.group_system')
@@ -107,8 +110,10 @@ class PaymentAcquirer(models.Model):
except requests.exceptions.ConnectionError:
_logger.exception("unable to reach endpoint at %s", url)
raise ValidationError("Adyen: " + _("Could not establish the connection to the API."))
- except requests.exceptions.HTTPError:
- _logger.exception("invalid API request at %s with data %s", url, payload)
+ except requests.exceptions.HTTPError as error:
+ _logger.exception(
+ "invalid API request at %s with data %s: %s", url, payload, error.response.text
+ )
raise ValidationError("Adyen: " + _("The communication with the API failed."))
return response.json()
diff --git a/addons/payment_adyen/models/payment_transaction.py b/addons/payment_adyen/models/payment_transaction.py
index e9bd9d4c0fd..cdf468717cc 100644
--- a/addons/payment_adyen/models/payment_transaction.py
+++ b/addons/payment_adyen/models/payment_transaction.py
@@ -15,11 +15,6 @@ _logger = logging.getLogger(__name__)
class PaymentTransaction(models.Model):
_inherit = 'payment.transaction'
- adyen_payment_data = fields.Char(
- string="Saved Payment Data",
- help="Data that must be passed back to Adyen when returning from redirect", readonly=True,
- groups='base.group_system')
-
#=== BUSINESS METHODS ===#
def _get_specific_processing_values(self, processing_values):
@@ -74,9 +69,8 @@ class PaymentTransaction(models.Model):
},
'reference': self.reference,
'paymentMethod': {
- 'recurringDetailReference': self.token_id.acquirer_ref
- }, # Required by Adyen although it is also provided with 'storedPaymentMethodId'
- 'storedPaymentMethodId': self.token_id.acquirer_ref,
+ 'recurringDetailReference': self.token_id.acquirer_ref,
+ },
'shopperReference': self.token_id.adyen_shopper_reference,
'recurringProcessingModel': 'Subscription',
'shopperIP': payment_utils.get_customer_ip_address(),
@@ -237,7 +231,7 @@ class PaymentTransaction(models.Model):
_logger.warning("An error occurred on transaction with reference %s (reason: %s)",
self.reference, refusal_reason)
self._set_error(
- _("An error occured during processing of your payment. Please try again.")
+ _("An error occurred during the processing of your payment. Please try again.")
)
elif payment_state in RESULT_CODES_MAPPING['refused']:
_logger.warning("The transaction with reference %s was refused (reason: %s)",
diff --git a/addons/payment_adyen/static/src/js/payment_form.js b/addons/payment_adyen/static/src/js/payment_form.js
index 398bf6f3f35..0a91c61ba1e 100644
--- a/addons/payment_adyen/static/src/js/payment_form.js
+++ b/addons/payment_adyen/static/src/js/payment_form.js
@@ -29,8 +29,7 @@ odoo.define('payment_adyen.payment_form', require => {
params: {
'acquirer_id': dropin.acquirerId,
'reference': this.adyenDropin.reference,
- 'details': state.data.details,
- 'payment_data': state.data.paymentData,
+ 'payment_details': state.data,
},
}).then(paymentDetails => {
if (paymentDetails.action) { // Additional action required from the shopper
@@ -133,13 +132,13 @@ odoo.define('payment_adyen.payment_form', require => {
// Overwrite the flow of the select payment option
this._setPaymentFlow('direct');
- // Get the acquirer state ("enabled" or "test")
+ // Get public information on the acquirer (state, client_key)
return this._rpc({
- route: '/payment/adyen/acquirer_state',
+ route: '/payment/adyen/acquirer_info',
params: {
'acquirer_id': paymentOptionId,
},
- }).then(acquirerState => {
+ }).then(acquirerInfo => {
// Get the available payment methods
return this._rpc({
route: '/payment/adyen/payment_methods',
@@ -154,38 +153,28 @@ odoo.define('payment_adyen.payment_form', require => {
: undefined,
},
}).then(paymentMethodsResult => {
- // Generate the origin key
- return this._rpc({
- route: '/payment/adyen/origin_key',
- params: {
- 'acquirer_id': paymentOptionId,
- },
- }).then(originKeyResult => {
- // Extract the origin key from the returned object
- const originKeyResultKeys = Object.keys(originKeyResult.originKeys);
- const originKey = originKeyResult.originKeys[originKeyResultKeys[0]];
-
- // Instantiate the drop-in
- const configuration = {
- paymentMethodsResponse: paymentMethodsResult,
- originKey: originKey,
- locale: (this._getContext().lang || 'en-US').replace('_', '-'),
- environment: acquirerState === 'enabled' ? 'live' : 'test',
+ // Instantiate the drop-in
+ const configuration = {
+ paymentMethodsResponse: paymentMethodsResult,
+ clientKey: acquirerInfo.client_key,
+ locale: (this._getContext().lang || 'en-US').replace('_', '-'),
+ environment: acquirerInfo.state === 'enabled' ? 'live' : 'test',
+ onAdditionalDetails: this._dropinOnAdditionalDetails.bind(this),
+ onError: this._dropinOnError.bind(this),
+ onSubmit: this._dropinOnSubmit.bind(this),
+ };
+ const checkout = new AdyenCheckout(configuration);
+ this.adyenDropin = checkout.create(
+ 'dropin', {
openFirstPaymentMethod: true,
openFirstStoredPaymentMethod: false,
showStoredPaymentMethods: false,
showPaymentMethods: true,
showPayButton: false,
- onAdditionalDetails: this._dropinOnAdditionalDetails.bind(this),
- onError: this._dropinOnError.bind(this),
- onSubmit: this._dropinOnSubmit.bind(this),
- };
- const checkout = new AdyenCheckout(configuration);
- this.adyenDropin = checkout.create('dropin').mount(
- `#o_adyen_dropin_container_${paymentOptionId}`
- );
- this.adyenDropin.acquirerId = paymentOptionId;
- });
+ setStatusAutomatically: true,
+ },
+ ).mount(`#o_adyen_dropin_container_${paymentOptionId}`);
+ this.adyenDropin.acquirerId = paymentOptionId;
});
}).guardedCatch((error) => {
error.event.preventDefault();
diff --git a/addons/payment_adyen/static/src/scss/dropin.scss b/addons/payment_adyen/static/src/scss/dropin.scss
new file mode 100644
index 00000000000..4cfcfe4fb88
--- /dev/null
+++ b/addons/payment_adyen/static/src/scss/dropin.scss
@@ -0,0 +1,6 @@
+.o_adyen_dropin {
+ [aria-hidden="true"], [aria-hidden="1"]
+ {
+ display: block !important;
+ }
+}
diff --git a/addons/payment_adyen/tests/common.py b/addons/payment_adyen/tests/common.py
index 45b2683d4b3..90e3e710104 100644
--- a/addons/payment_adyen/tests/common.py
+++ b/addons/payment_adyen/tests/common.py
@@ -11,6 +11,7 @@ class AdyenCommon(PaymentCommon):
cls.adyen = cls._prepare_acquirer('adyen', update_values={
'adyen_merchant_account': 'dummy',
'adyen_api_key': 'dummy',
+ 'adyen_client_key': 'dummy',
'adyen_hmac_key': 'dummy',
'adyen_checkout_api_url': 'https://this.is.an.url',
'adyen_recurring_api_url': 'https://this.is.an.url',
diff --git a/addons/payment_adyen/views/payment_adyen_templates.xml b/addons/payment_adyen/views/payment_adyen_templates.xml
index 5213c2deb16..6eb1c08e07d 100644
--- a/addons/payment_adyen/views/payment_adyen_templates.xml
+++ b/addons/payment_adyen/views/payment_adyen_templates.xml
@@ -2,7 +2,7 @@
-
+
diff --git a/addons/payment_adyen/views/payment_views.xml b/addons/payment_adyen/views/payment_views.xml
index 876d360e9d7..78e511aeeff 100644
--- a/addons/payment_adyen/views/payment_views.xml
+++ b/addons/payment_adyen/views/payment_views.xml
@@ -10,6 +10,7 @@
+