[IMP] payment_adyen: replace API URLs with an account-specific URL prefix

Before this commit, users were required to compute the API URLs, which is
specific to each Adyen account, themselves. After this commit, users
will be able to copy their account prefix from Adyen to automatically
generate the API URLs.

task-3338126

closes odoo/odoo#126831

Related: odoo/upgrade#4876
Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
This commit is contained in:
Anita (anko)
2023-09-22 17:35:47 +00:00
committed by Antoine Vandevenne (anv)
parent bad4eea5d3
commit 428a2cda67
5 changed files with 32 additions and 51 deletions
+3 -12
View File
@@ -59,10 +59,7 @@ class AdyenController(http.Controller):
'channel': 'Web',
}
response_content = provider_sudo._adyen_make_request(
url_field_name='adyen_checkout_api_url',
endpoint='/paymentMethods',
payload=data,
method='POST'
endpoint='/paymentMethods', payload=data, method='POST'
)
_logger.info("paymentMethods request response:\n%s", pprint.pformat(response_content))
return response_content
@@ -139,10 +136,7 @@ class AdyenController(http.Controller):
# Make the payment request to Adyen
response_content = provider_sudo._adyen_make_request(
url_field_name='adyen_checkout_api_url',
endpoint='/payments',
payload=data,
method='POST'
endpoint='/payments', payload=data, method='POST'
)
# Handle the payment request response
@@ -171,10 +165,7 @@ class AdyenController(http.Controller):
# Make the payment details request to Adyen
provider_sudo = request.env['payment.provider'].browse(provider_id).sudo()
response_content = provider_sudo._adyen_make_request(
url_field_name='adyen_checkout_api_url',
endpoint='/payments/details',
payload=payment_details,
method='POST'
endpoint='/payments/details', payload=payment_details, method='POST'
)
# Handle the payment details request response
+26 -28
View File
@@ -31,35 +31,35 @@ class PaymentProvider(models.Model):
adyen_hmac_key = fields.Char(
string="HMAC Key", help="The HMAC key of the webhook", required_if_provider='adyen',
groups='base.group_system')
adyen_checkout_api_url = fields.Char(
string="Checkout API URL", help="The base URL for the Checkout API endpoints",
required_if_provider='adyen')
adyen_recurring_api_url = fields.Char(
string="Recurring API URL", help="The base URL for the Recurring API endpoints",
required_if_provider='adyen')
adyen_api_url_prefix = fields.Char(
string="API URL Prefix",
help="The base URL for the API endpoints",
required_if_provider='adyen',
)
#=== CRUD METHODS ===#
@api.model_create_multi
def create(self, values_list):
for values in values_list:
self._adyen_trim_api_urls(values)
self._adyen_extract_prefix_from_api_url(values)
return super().create(values_list)
def write(self, values):
self._adyen_trim_api_urls(values)
self._adyen_extract_prefix_from_api_url(values)
return super().write(values)
@api.model
def _adyen_trim_api_urls(self, values):
""" Remove the version and the endpoint from the url of Adyen API fields.
def _adyen_extract_prefix_from_api_url(self, values):
""" Update the create or write values with the prefix extracted from the API URL.
:param dict values: The create or write values
:param dict values: The create or write values.
:return: None
"""
for field_name in ('adyen_checkout_api_url', 'adyen_recurring_api_url'):
if values.get(field_name): # Test the value in case we're duplicating a provider
values[field_name] = re.sub(r'[vV]\d+(/.*)?', '', values[field_name])
if values.get('adyen_api_url_prefix'): # Test if we're duplicating a provider.
values['adyen_api_url_prefix'] = re.sub(
r'(?:https://)?(\w+-\w+).*', r'\1', values['adyen_api_url_prefix']
)
#=== COMPUTE METHODS ===#
@@ -74,14 +74,11 @@ class PaymentProvider(models.Model):
#=== BUSINESS METHODS - PAYMENT FLOW ===#
def _adyen_make_request(
self, url_field_name, endpoint, endpoint_param=None, payload=None, method='POST'
):
def _adyen_make_request(self, endpoint, endpoint_param=None, payload=None, method='POST'):
""" Make a request to Adyen API at the specified endpoint.
Note: self.ensure_one()
:param str url_field_name: The name of the field holding the base URL for the request
:param str endpoint: The endpoint to be reached by the request
:param str endpoint_param: A variable required by some endpoints which are interpolated with
it if provided. For example, the provider reference of the source
@@ -93,27 +90,28 @@ class PaymentProvider(models.Model):
:raise: ValidationError if an HTTP error occurs
"""
def _build_url(_base_url, _version, _endpoint):
def _build_url(prefix_, version_, endpoint_):
""" Build an API URL by appending the version and endpoint to a base URL.
The final URL follows this pattern: `<_base>/V<_version>/<_endpoint>`.
:param str _base_url: The base of the url prefixed with `https://`
:param int _version: The version of the endpoint
:param str _endpoint: The endpoint of the URL.
:return: The final URL
:param str prefix_: The API URL prefix of the account.
:param int version_: The version of the endpoint.
:param str endpoint_: The endpoint of the URL.
:return: The final URL.
:rtype: str
"""
_base = _base_url.rstrip('/') # Remove potential trailing slash
_endpoint = _endpoint.lstrip('/') # Remove potential leading slash
return f'{_base}/V{_version}/{_endpoint}'
prefix_ = prefix_.rstrip('/') # Remove potential trailing slash
endpoint_ = endpoint_.lstrip('/') # Remove potential leading slash
test_mode_ = self.state == 'test'
prefix_ = f'{prefix_}.adyen' if test_mode_ else f'{prefix_}-checkout-live.adyenpayments'
return f'https://{prefix_}.com/checkout/V{version_}/{endpoint_}'
self.ensure_one()
base_url = self[url_field_name] # Restrict request URL to the stored API URL fields
version = const.API_ENDPOINT_VERSIONS[endpoint]
endpoint = endpoint if not endpoint_param else endpoint.format(endpoint_param)
url = _build_url(base_url, version, endpoint)
url = _build_url(self.adyen_api_url_prefix, version, endpoint)
headers = {'X-API-Key': self.adyen_api_key}
try:
response = requests.request(method, url, json=payload, headers=headers, timeout=60)
@@ -96,10 +96,7 @@ class PaymentTransaction(models.Model):
# Make the payment request to Adyen
try:
response_content = self.provider_id._adyen_make_request(
url_field_name='adyen_checkout_api_url',
endpoint='/payments',
payload=data,
method='POST',
endpoint='/payments', payload=data, method='POST'
)
except ValidationError as e:
if self.operation == 'offline':
@@ -143,7 +140,6 @@ class PaymentTransaction(models.Model):
'reference': refund_tx.reference,
}
response_content = refund_tx.provider_id._adyen_make_request(
url_field_name='adyen_checkout_api_url',
endpoint='/payments/{}/refunds',
endpoint_param=self.provider_reference,
payload=data,
@@ -183,7 +179,6 @@ class PaymentTransaction(models.Model):
'reference': self.reference,
}
response_content = self.provider_id._adyen_make_request(
url_field_name='adyen_checkout_api_url',
endpoint='/payments/{}/captures',
endpoint_param=self.provider_reference,
payload=data,
@@ -219,7 +214,6 @@ class PaymentTransaction(models.Model):
'reference': self.reference,
}
response_content = self.provider_id._adyen_make_request(
url_field_name='adyen_checkout_api_url',
endpoint='/payments/{}/cancels',
endpoint_param=self.provider_reference,
payload=data,
+1 -2
View File
@@ -14,8 +14,7 @@ class AdyenCommon(PaymentCommon):
'adyen_api_key': 'dummy',
'adyen_client_key': 'dummy',
'adyen_hmac_key': '12345678',
'adyen_checkout_api_url': 'https://this.is.an.url',
'adyen_recurring_api_url': 'https://this.is.an.url',
'adyen_api_url_prefix': 'prefix',
})
# Override default values
@@ -12,8 +12,7 @@
<field name="adyen_api_key" required="code == 'adyen' and state != 'disabled'" password="True"/>
<field name="adyen_client_key" required="code == 'adyen' and state != 'disabled'"/>
<field name="adyen_hmac_key" required="code == 'adyen' and state != 'disabled'" password="True"/>
<field name="adyen_checkout_api_url" required="code == 'adyen' and state != 'disabled'"/>
<field name="adyen_recurring_api_url" required="code == 'adyen' and state != 'disabled'"/>
<field name="adyen_api_url_prefix" required="code == 'adyen' and state != 'disabled'"/>
</group>
</group>
<group name="provider_config" position='before'>