199 lines
8.1 KiB
Python
199 lines
8.1 KiB
Python
# coding: utf-8
|
|
from hashlib import sha1
|
|
import logging
|
|
|
|
from werkzeug import urls
|
|
|
|
from flectra import api, fields, models, _
|
|
from flectra.addons.payment.models.payment_acquirer import ValidationError
|
|
from flectra.addons.payment_buckaroo.controllers.main import BuckarooController
|
|
|
|
from flectra.tools.float_utils import float_compare
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
def normalize_keys_upper(data):
|
|
"""Set all keys of a dictionnary to uppercase
|
|
|
|
Buckaroo parameters names are case insensitive
|
|
convert everything to upper case to be able to easily detected the presence
|
|
of a parameter by checking the uppercase key only
|
|
"""
|
|
return {key.upper(): val for key, val in data.items()}
|
|
|
|
|
|
class AcquirerBuckaroo(models.Model):
|
|
_inherit = 'payment.acquirer'
|
|
|
|
provider = fields.Selection(selection_add=[('buckaroo', 'Buckaroo')])
|
|
brq_websitekey = fields.Char('WebsiteKey', required_if_provider='buckaroo', groups='base.group_user')
|
|
brq_secretkey = fields.Char('SecretKey', required_if_provider='buckaroo', groups='base.group_user')
|
|
|
|
def _get_buckaroo_urls(self, environment):
|
|
""" Buckaroo URLs
|
|
"""
|
|
if environment == 'prod':
|
|
return {
|
|
'buckaroo_form_url': 'https://checkout.buckaroo.nl/html/',
|
|
}
|
|
else:
|
|
return {
|
|
'buckaroo_form_url': 'https://testcheckout.buckaroo.nl/html/',
|
|
}
|
|
|
|
def _buckaroo_generate_digital_sign(self, inout, values):
|
|
""" Generate the shasign for incoming or outgoing communications.
|
|
|
|
:param browse acquirer: the payment.acquirer browse record. It should
|
|
have a shakey in shaky out
|
|
:param string inout: 'in' (flectra contacting buckaroo) or 'out' (buckaroo
|
|
contacting flectra).
|
|
:param dict values: transaction values
|
|
|
|
:return string: shasign
|
|
"""
|
|
assert inout in ('in', 'out')
|
|
assert self.provider == 'buckaroo'
|
|
|
|
keys = "add_returndata Brq_amount Brq_culture Brq_currency Brq_invoicenumber Brq_return Brq_returncancel Brq_returnerror Brq_returnreject brq_test Brq_websitekey".split()
|
|
|
|
def get_value(key):
|
|
if values.get(key):
|
|
return values[key]
|
|
return ''
|
|
|
|
values = dict(values or {})
|
|
|
|
if inout == 'out':
|
|
for key in list(values):
|
|
# case insensitive keys
|
|
if key.upper() == 'BRQ_SIGNATURE':
|
|
del values[key]
|
|
break
|
|
|
|
items = sorted(values.items(), key=lambda pair: pair[0].lower())
|
|
sign = ''.join('%s=%s' % (k, urls.url_unquote_plus(v)) for k, v in items)
|
|
else:
|
|
sign = ''.join('%s=%s' % (k, get_value(k)) for k in keys)
|
|
# Add the pre-shared secret key at the end of the signature
|
|
sign = sign + self.brq_secretkey
|
|
shasign = sha1(sign.encode('utf-8')).hexdigest()
|
|
return shasign
|
|
|
|
@api.multi
|
|
def buckaroo_form_generate_values(self, values):
|
|
base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
|
|
if self.env.ref('base.module_website').state == 'installed':
|
|
base_url = "http://" + self.env['website'].get_current_website().domain
|
|
buckaroo_tx_values = dict(values)
|
|
buckaroo_tx_values.update({
|
|
'Brq_websitekey': self.brq_websitekey,
|
|
'Brq_amount': values['amount'],
|
|
'Brq_currency': values['currency'] and values['currency'].name or '',
|
|
'Brq_invoicenumber': values['reference'],
|
|
'brq_test': False if self.environment == 'prod' else True,
|
|
'Brq_return': urls.url_join(base_url, BuckarooController._return_url),
|
|
'Brq_returncancel': urls.url_join(base_url, BuckarooController._cancel_url),
|
|
'Brq_returnerror': urls.url_join(base_url, BuckarooController._exception_url),
|
|
'Brq_returnreject': urls.url_join(base_url, BuckarooController._reject_url),
|
|
'Brq_culture': (values.get('partner_lang') or 'en_US').replace('_', '-'),
|
|
'add_returndata': buckaroo_tx_values.pop('return_url', '') or '',
|
|
})
|
|
buckaroo_tx_values['Brq_signature'] = self._buckaroo_generate_digital_sign('in', buckaroo_tx_values)
|
|
return buckaroo_tx_values
|
|
|
|
@api.multi
|
|
def buckaroo_get_form_action_url(self):
|
|
return self._get_buckaroo_urls(self.environment)['buckaroo_form_url']
|
|
|
|
|
|
class TxBuckaroo(models.Model):
|
|
_inherit = 'payment.transaction'
|
|
|
|
# buckaroo status
|
|
_buckaroo_valid_tx_status = [190]
|
|
_buckaroo_pending_tx_status = [790, 791, 792, 793]
|
|
_buckaroo_cancel_tx_status = [890, 891]
|
|
_buckaroo_error_tx_status = [490, 491, 492]
|
|
_buckaroo_reject_tx_status = [690]
|
|
|
|
# --------------------------------------------------
|
|
# FORM RELATED METHODS
|
|
# --------------------------------------------------
|
|
|
|
@api.model
|
|
def _buckaroo_form_get_tx_from_data(self, data):
|
|
""" Given a data dict coming from buckaroo, verify it and find the related
|
|
transaction record. """
|
|
origin_data = dict(data)
|
|
data = normalize_keys_upper(data)
|
|
reference, pay_id, shasign = data.get('BRQ_INVOICENUMBER'), data.get('BRQ_PAYMENT'), data.get('BRQ_SIGNATURE')
|
|
if not reference or not pay_id or not shasign:
|
|
error_msg = _('Buckaroo: received data with missing reference (%s) or pay_id (%s) or shasign (%s)') % (reference, pay_id, shasign)
|
|
_logger.info(error_msg)
|
|
raise ValidationError(error_msg)
|
|
|
|
tx = self.search([('reference', '=', reference)])
|
|
if not tx or len(tx) > 1:
|
|
error_msg = _('Buckaroo: received data for reference %s') % (reference)
|
|
if not tx:
|
|
error_msg += _('; no order found')
|
|
else:
|
|
error_msg += _('; multiple order found')
|
|
_logger.info(error_msg)
|
|
raise ValidationError(error_msg)
|
|
|
|
# verify shasign
|
|
shasign_check = tx.acquirer_id._buckaroo_generate_digital_sign('out', origin_data)
|
|
if shasign_check.upper() != shasign.upper():
|
|
error_msg = _('Buckaroo: invalid shasign, received %s, computed %s, for data %s') % (shasign, shasign_check, data)
|
|
_logger.info(error_msg)
|
|
raise ValidationError(error_msg)
|
|
|
|
return tx
|
|
|
|
def _buckaroo_form_get_invalid_parameters(self, data):
|
|
invalid_parameters = []
|
|
data = normalize_keys_upper(data)
|
|
if self.acquirer_reference and data.get('BRQ_TRANSACTIONS') != self.acquirer_reference:
|
|
invalid_parameters.append(('Transaction Id', data.get('BRQ_TRANSACTIONS'), self.acquirer_reference))
|
|
# check what is buyed
|
|
if float_compare(float(data.get('BRQ_AMOUNT', '0.0')), self.amount, 2) != 0:
|
|
invalid_parameters.append(('Amount', data.get('BRQ_AMOUNT'), '%.2f' % self.amount))
|
|
if data.get('BRQ_CURRENCY') != self.currency_id.name:
|
|
invalid_parameters.append(('Currency', data.get('BRQ_CURRENCY'), self.currency_id.name))
|
|
|
|
return invalid_parameters
|
|
|
|
def _buckaroo_form_validate(self, data):
|
|
data = normalize_keys_upper(data)
|
|
status_code = int(data.get('BRQ_STATUSCODE', '0'))
|
|
if status_code in self._buckaroo_valid_tx_status:
|
|
self.write({
|
|
'state': 'done',
|
|
'acquirer_reference': data.get('BRQ_TRANSACTIONS'),
|
|
})
|
|
return True
|
|
elif status_code in self._buckaroo_pending_tx_status:
|
|
self.write({
|
|
'state': 'pending',
|
|
'acquirer_reference': data.get('BRQ_TRANSACTIONS'),
|
|
})
|
|
return True
|
|
elif status_code in self._buckaroo_cancel_tx_status:
|
|
self.write({
|
|
'state': 'cancel',
|
|
'acquirer_reference': data.get('BRQ_TRANSACTIONS'),
|
|
})
|
|
return True
|
|
else:
|
|
error = 'Buckaroo: feedback error'
|
|
_logger.info(error)
|
|
self.write({
|
|
'state': 'error',
|
|
'state_message': error,
|
|
'acquirer_reference': data.get('BRQ_TRANSACTIONS'),
|
|
})
|
|
return False
|