142 lines
5.8 KiB
Python
142 lines
5.8 KiB
Python
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||
|
|
||
|
import logging
|
||
|
from hashlib import new as hashnew
|
||
|
|
||
|
import requests
|
||
|
|
||
|
from odoo import _, api, fields, models
|
||
|
from odoo.exceptions import ValidationError
|
||
|
|
||
|
from odoo.addons.payment_ogone import const
|
||
|
|
||
|
|
||
|
_logger = logging.getLogger(__name__)
|
||
|
|
||
|
|
||
|
class PaymentProvider(models.Model):
|
||
|
_inherit = 'payment.provider'
|
||
|
|
||
|
code = fields.Selection(
|
||
|
selection_add=[('ogone', "Ogone")], ondelete={'ogone': 'set default'})
|
||
|
ogone_pspid = fields.Char(
|
||
|
string="PSPID", help="The ID solely used to identify the account with Ogone",
|
||
|
required_if_provider='ogone')
|
||
|
ogone_userid = fields.Char(
|
||
|
string="API User ID", help="The ID solely used to identify the API user with Ogone",
|
||
|
required_if_provider='ogone')
|
||
|
ogone_password = fields.Char(
|
||
|
string="API User Password", required_if_provider='ogone', groups='base.group_system')
|
||
|
ogone_shakey_in = fields.Char(
|
||
|
string="SHA Key IN", required_if_provider='ogone', groups='base.group_system')
|
||
|
ogone_shakey_out = fields.Char(
|
||
|
string="SHA Key OUT", required_if_provider='ogone', groups='base.group_system')
|
||
|
ogone_hash_function = fields.Selection(
|
||
|
[('sha1', 'SHA1'), ('sha256', 'SHA256'), ('sha512', 'SHA512')], default='sha512',
|
||
|
string="Hash function", required_if_provider='ogone',
|
||
|
)
|
||
|
|
||
|
#=== COMPUTE METHODS ===#
|
||
|
|
||
|
def _compute_feature_support_fields(self):
|
||
|
""" Override of `payment` to enable additional features. """
|
||
|
super()._compute_feature_support_fields()
|
||
|
self.filtered(lambda p: p.code == 'ogone').update({
|
||
|
'support_tokenization': True,
|
||
|
})
|
||
|
|
||
|
#=== BUSINESS METHODS ===#
|
||
|
|
||
|
@api.model
|
||
|
def _get_compatible_providers(self, *args, is_validation=False, **kwargs):
|
||
|
""" Override of payment to unlist Ogone providers for validation operations. """
|
||
|
providers = super()._get_compatible_providers(*args, is_validation=is_validation, **kwargs)
|
||
|
|
||
|
if is_validation:
|
||
|
providers = providers.filtered(lambda p: p.code != 'ogone')
|
||
|
|
||
|
return providers
|
||
|
|
||
|
def _ogone_get_api_url(self, api_key):
|
||
|
""" Return the appropriate URL of the requested API for the provider state.
|
||
|
|
||
|
Note: self.ensure_one()
|
||
|
|
||
|
:param str api_key: The API whose URL to get: 'hosted_payment_page' or 'directlink'
|
||
|
:return: The API URL
|
||
|
:rtype: str
|
||
|
"""
|
||
|
self.ensure_one()
|
||
|
|
||
|
if self.state == 'enabled':
|
||
|
api_urls = {
|
||
|
'hosted_payment_page': 'https://secure.ogone.com/ncol/prod/orderstandard_utf8.asp',
|
||
|
'directlink': 'https://secure.ogone.com/ncol/prod/orderdirect_utf8.asp',
|
||
|
}
|
||
|
else: # 'test'
|
||
|
api_urls = {
|
||
|
'hosted_payment_page': 'https://ogone.test.v-psp.com/ncol/test/orderstandard_utf8.asp',
|
||
|
'directlink': 'https://ogone.test.v-psp.com/ncol/test/orderdirect_utf8.asp',
|
||
|
}
|
||
|
return api_urls.get(api_key)
|
||
|
|
||
|
def _ogone_generate_signature(self, values, incoming=True, format_keys=False):
|
||
|
""" Generate the signature for incoming or outgoing communications.
|
||
|
|
||
|
:param dict values: The values used to generate the signature
|
||
|
:param bool incoming: Whether the signature must be generated for an incoming (Ogone to
|
||
|
Odoo) or outgoing (Odoo to Ogone) communication.
|
||
|
:param bool format_keys: Whether the keys must be formatted as uppercase, dot-separated
|
||
|
strings to comply with Ogone APIs. This must be used when the keys
|
||
|
are formatted as underscore-separated strings to be compliant with
|
||
|
QWeb's `t-att-value`.
|
||
|
:return: The signature
|
||
|
:rtype: str
|
||
|
"""
|
||
|
|
||
|
def _filter_key(_key):
|
||
|
return not incoming or _key in const.VALID_KEYS
|
||
|
|
||
|
key = self.ogone_shakey_out if incoming else self.ogone_shakey_in # Swapped for Ogone's POV
|
||
|
if format_keys:
|
||
|
formatted_items = [(k.upper().replace('_', '.'), v) for k, v in values.items()]
|
||
|
else:
|
||
|
formatted_items = [(k.upper(), v) for k, v in values.items()]
|
||
|
sorted_items = sorted(formatted_items)
|
||
|
signing_string = ''.join(f'{k}={v}{key}' for k, v in sorted_items if _filter_key(k) and v)
|
||
|
shasign = hashnew(self.ogone_hash_function)
|
||
|
shasign.update(signing_string.encode())
|
||
|
return shasign.hexdigest()
|
||
|
|
||
|
def _ogone_make_request(self, payload=None, method='POST'):
|
||
|
""" Make a request to one of Ogone APIs.
|
||
|
|
||
|
Note: self.ensure_one()
|
||
|
|
||
|
:param dict payload: The payload of the request
|
||
|
:param str method: The HTTP method of the request
|
||
|
:return The content of the response
|
||
|
:rtype: bytes
|
||
|
:raise: ValidationError if an HTTP error occurs
|
||
|
"""
|
||
|
self.ensure_one()
|
||
|
|
||
|
url = self._ogone_get_api_url('directlink')
|
||
|
try:
|
||
|
response = requests.request(method, url, data=payload, timeout=60)
|
||
|
response.raise_for_status()
|
||
|
except requests.exceptions.ConnectionError:
|
||
|
_logger.exception("unable to reach endpoint at %s", url)
|
||
|
raise ValidationError("Ogone: " + _("Could not establish the connection to the API."))
|
||
|
except requests.exceptions.HTTPError:
|
||
|
_logger.exception("invalid API request at %s with data %s", url, payload)
|
||
|
raise ValidationError("Ogone: " + _("The communication with the API failed."))
|
||
|
return response.content
|
||
|
|
||
|
def _get_default_payment_method_codes(self):
|
||
|
""" Override of `payment` to return the default payment method codes. """
|
||
|
default_codes = super()._get_default_payment_method_codes()
|
||
|
if self.code != 'ogone':
|
||
|
return default_codes
|
||
|
return const.DEFAULT_PAYMENT_METHODS_CODES
|