[MIG] payment_adyen: new API
No functional change.
This commit is contained in:
@@ -5,8 +5,8 @@ import logging
|
||||
import pprint
|
||||
import werkzeug
|
||||
|
||||
from openerp import http, SUPERUSER_ID
|
||||
from openerp.http import request
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -20,7 +20,7 @@ class AdyenController(http.Controller):
|
||||
def adyen_return(self, **post):
|
||||
_logger.info('Beginning Adyen form_feedback with post data %s', pprint.pformat(post)) # debug
|
||||
if post.get('authResult') not in ['CANCELLED']:
|
||||
request.registry['payment.transaction'].form_feedback(request.cr, SUPERUSER_ID, post, 'adyen', context=request.context)
|
||||
request.env['payment.transaction'].sudo().form_feedback(post, 'adyen')
|
||||
return_url = post.pop('return_url', '')
|
||||
if not return_url:
|
||||
custom = json.loads(post.pop('merchantReturnData', '{}'))
|
||||
@@ -31,9 +31,8 @@ class AdyenController(http.Controller):
|
||||
'/payment/adyen/notification',
|
||||
], type='http', auth='none', methods=['POST'], csrf=False)
|
||||
def adyen_notification(self, **post):
|
||||
tx_id = post.get('merchantReference') and request.registry['payment.transaction'].search(request.cr, SUPERUSER_ID, [('reference', 'in', [post.get('merchantReference')])], limit=1, context=request.context)
|
||||
if post.get('eventCode') in ['AUTHORISATION'] and tx_id:
|
||||
tx = request.registry['payment.transaction'].browse(request.cr, SUPERUSER_ID, tx_id, context=request.context)
|
||||
tx = post.get('merchantReference') and request.env['payment.transaction'].sudo().search([('reference', 'in', [post.get('merchantReference')])], limit=1)
|
||||
if post.get('eventCode') in ['AUTHORISATION'] and tx:
|
||||
states = (post.get('merchantReference'), post.get('success'), tx.state)
|
||||
if (post.get('success') == 'true' and tx.state == 'done') or (post.get('success') == 'false' and tx.state in ['cancel', 'error']):
|
||||
_logger.info('Notification from Adyen for the reference %s: received %s, state is %s', states)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="payment.payment_acquirer_adyen" model="payment.acquirer">
|
||||
@@ -17,4 +17,4 @@
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
</odoo>
|
||||
|
||||
@@ -9,46 +9,35 @@ import hmac
|
||||
import logging
|
||||
import urlparse
|
||||
|
||||
from openerp.addons.payment.models.payment_acquirer import ValidationError
|
||||
from openerp.addons.payment_adyen.controllers.main import AdyenController
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.tools import float_round
|
||||
from openerp.tools.translate import _
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.addons.payment.models.payment_acquirer import ValidationError
|
||||
from odoo.addons.payment_adyen.controllers.main import AdyenController
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AcquirerAdyen(osv.Model):
|
||||
class AcquirerAdyen(models.Model):
|
||||
_inherit = 'payment.acquirer'
|
||||
|
||||
def _get_adyen_urls(self, cr, uid, environment, context=None):
|
||||
""" Adyen URLs
|
||||
provider = fields.Selection(selection_add=[('adyen', 'Adyen')])
|
||||
adyen_merchant_account = fields.Char('Merchant Account', required_if_provider='adyen')
|
||||
adyen_skin_code = fields.Char('Skin Code', required_if_provider='adyen')
|
||||
adyen_skin_hmac_key = fields.Char('Skin HMAC Key', required_if_provider='adyen')
|
||||
|
||||
- yhpp: hosted payment page: pay.shtml for single, select.shtml for multiple
|
||||
"""
|
||||
@api.model
|
||||
def _get_adyen_urls(self, environment):
|
||||
""" Adyen URLs: yhpp: hosted payment page: pay.shtml for single, select.shtml for multiple """
|
||||
return {
|
||||
'adyen_form_url': 'https://%s.adyen.com/hpp/pay.shtml' % ('live' if environment == 'prod' else environment),
|
||||
}
|
||||
|
||||
def _get_providers(self, cr, uid, context=None):
|
||||
providers = super(AcquirerAdyen, self)._get_providers(cr, uid, context=context)
|
||||
providers.append(['adyen', 'Adyen'])
|
||||
return providers
|
||||
|
||||
_columns = {
|
||||
'adyen_merchant_account': fields.char('Merchant Account', required_if_provider='adyen'),
|
||||
'adyen_skin_code': fields.char('Skin Code', required_if_provider='adyen'),
|
||||
'adyen_skin_hmac_key': fields.char('Skin HMAC Key', required_if_provider='adyen'),
|
||||
}
|
||||
|
||||
def _adyen_generate_merchant_sig_sha256(self, acquirer, inout, values):
|
||||
@api.multi
|
||||
def _adyen_generate_merchant_sig_sha256(self, inout, values):
|
||||
""" Generate the shasign for incoming or outgoing communications., when using the SHA-256
|
||||
signature.
|
||||
|
||||
:param browse acquirer: the payment.acquirer browse record. It should
|
||||
have a shakey in shaky out
|
||||
:param string inout: 'in' (openerp contacting ogone) or 'out' (adyen
|
||||
contacting openerp). In this last case only some
|
||||
:param string inout: 'in' (odoo contacting ogone) or 'out' (adyen
|
||||
contacting odoo). In this last case only some
|
||||
fields should be contained (see e-Commerce basic)
|
||||
:param dict values: transaction values
|
||||
:return string: shasign
|
||||
@@ -62,7 +51,7 @@ class AcquirerAdyen(osv.Model):
|
||||
return base64.b64encode(hm.digest())
|
||||
|
||||
assert inout in ('in', 'out')
|
||||
assert acquirer.provider == 'adyen'
|
||||
assert self.provider == 'adyen'
|
||||
|
||||
if inout == 'in':
|
||||
# All the fields sent to Adyen must be included in the signature. ALL the fucking
|
||||
@@ -83,27 +72,26 @@ class AcquirerAdyen(osv.Model):
|
||||
'pspReference', 'shopperLocale', 'skinCode',
|
||||
]
|
||||
|
||||
hmac_key = binascii.a2b_hex(acquirer.adyen_skin_hmac_key.encode('ascii'))
|
||||
hmac_key = binascii.a2b_hex(self.adyen_skin_hmac_key.encode('ascii'))
|
||||
raw_values = {k: values.get(k.encode('ascii'), '') for k in keys if k in values}
|
||||
raw_values_ordered = OrderedDict(sorted(raw_values.items(), key=lambda t: t[0]))
|
||||
|
||||
return signParams(raw_values_ordered)
|
||||
|
||||
def _adyen_generate_merchant_sig(self, acquirer, inout, values):
|
||||
@api.multi
|
||||
def _adyen_generate_merchant_sig(self, inout, values):
|
||||
""" Generate the shasign for incoming or outgoing communications, when using the SHA-1
|
||||
signature (deprecated by Adyen).
|
||||
|
||||
:param browse acquirer: the payment.acquirer browse record. It should
|
||||
have a shakey in shaky out
|
||||
:param string inout: 'in' (openerp contacting ogone) or 'out' (adyen
|
||||
contacting openerp). In this last case only some
|
||||
:param string inout: 'in' (odoo contacting ogone) or 'out' (adyen
|
||||
contacting odoo). In this last case only some
|
||||
fields should be contained (see e-Commerce basic)
|
||||
:param dict values: transaction values
|
||||
|
||||
:return string: shasign
|
||||
"""
|
||||
assert inout in ('in', 'out')
|
||||
assert acquirer.provider == 'adyen'
|
||||
assert self.provider == 'adyen'
|
||||
|
||||
if inout == 'in':
|
||||
keys = "paymentAmount currencyCode shipBeforeDate merchantReference skinCode merchantAccount sessionValidity shopperEmail shopperReference recurringContract allowedMethods blockedMethods shopperStatement merchantReturnData billingAddressType deliveryAddressType offset".split()
|
||||
@@ -116,66 +104,67 @@ class AcquirerAdyen(osv.Model):
|
||||
return ''
|
||||
|
||||
sign = ''.join('%s' % get_value(k) for k in keys).encode('ascii')
|
||||
key = acquirer.adyen_skin_hmac_key.encode('ascii')
|
||||
key = self.adyen_skin_hmac_key.encode('ascii')
|
||||
return base64.b64encode(hmac.new(key, sign, hashlib.sha1).digest())
|
||||
|
||||
def adyen_form_generate_values(self, cr, uid, ids, values, context=None):
|
||||
base_url = self.pool['ir.config_parameter'].get_param(cr, uid, 'web.base.url')
|
||||
acquirer = self.browse(cr, uid, ids, context=context)[0]
|
||||
@api.multi
|
||||
def adyen_form_generate_values(self, values):
|
||||
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||
# tmp
|
||||
import datetime
|
||||
from dateutil import relativedelta
|
||||
|
||||
if acquirer.provider == 'adyen' and len(acquirer.adyen_skin_hmac_key) == 64:
|
||||
if self.provider == 'adyen' and len(self.adyen_skin_hmac_key) == 64:
|
||||
tmp_date = datetime.datetime.today() + relativedelta.relativedelta(days=1)
|
||||
|
||||
values.update({
|
||||
'merchantReference': values['reference'],
|
||||
'paymentAmount': '%d' % int(float_round(values['amount'], 2) * 100),
|
||||
'paymentAmount': '%d' % int(tools.float_round(values['amount'], 2) * 100),
|
||||
'currencyCode': values['currency'] and values['currency'].name or '',
|
||||
'shipBeforeDate': tmp_date.strftime('%Y-%m-%d'),
|
||||
'skinCode': acquirer.adyen_skin_code,
|
||||
'merchantAccount': acquirer.adyen_merchant_account,
|
||||
'skinCode': self.adyen_skin_code,
|
||||
'merchantAccount': self.adyen_merchant_account,
|
||||
'shopperLocale': values.get('partner_lang', ''),
|
||||
'sessionValidity': tmp_date.isoformat('T')[:19] + "Z",
|
||||
'resURL': '%s' % urlparse.urljoin(base_url, AdyenController._return_url),
|
||||
'merchantReturnData': json.dumps({'return_url': '%s' % values.pop('return_url')}) if values.get('return_url', '') else False,
|
||||
'shopperEmail': values.get('partner_email', ''),
|
||||
})
|
||||
values['merchantSig'] = self._adyen_generate_merchant_sig_sha256(acquirer, 'in', values)
|
||||
values['merchantSig'] = self._adyen_generate_merchant_sig_sha256('in', values)
|
||||
|
||||
else:
|
||||
tmp_date = datetime.date.today() + relativedelta.relativedelta(days=1)
|
||||
|
||||
values.update({
|
||||
'merchantReference': values['reference'],
|
||||
'paymentAmount': '%d' % int(float_round(values['amount'], 2) * 100),
|
||||
'paymentAmount': '%d' % int(tools.float_round(values['amount'], 2) * 100),
|
||||
'currencyCode': values['currency'] and values['currency'].name or '',
|
||||
'shipBeforeDate': tmp_date,
|
||||
'skinCode': acquirer.adyen_skin_code,
|
||||
'merchantAccount': acquirer.adyen_merchant_account,
|
||||
'skinCode': self.adyen_skin_code,
|
||||
'merchantAccount': self.adyen_merchant_account,
|
||||
'shopperLocale': values.get('partner_lang'),
|
||||
'sessionValidity': tmp_date,
|
||||
'resURL': '%s' % urlparse.urljoin(base_url, AdyenController._return_url),
|
||||
'merchantReturnData': json.dumps({'return_url': '%s' % values.pop('return_url')}) if values.get('return_url') else False,
|
||||
'merchantSig': self._adyen_generate_merchant_sig(acquirer, 'in', values),
|
||||
'merchantSig': self._adyen_generate_merchant_sig('in', values),
|
||||
})
|
||||
|
||||
return values
|
||||
|
||||
def adyen_get_form_action_url(self, cr, uid, ids, context=None):
|
||||
acquirer = self.browse(cr, uid, ids, context=context)[0]
|
||||
return self._get_adyen_urls(cr, uid, acquirer.environment, context=context)['adyen_form_url']
|
||||
@api.multi
|
||||
def adyen_get_form_action_url(self):
|
||||
return self._get_adyen_urls(self.environment)['adyen_form_url']
|
||||
|
||||
|
||||
class TxAdyen(osv.Model):
|
||||
class TxAdyen(models.Model):
|
||||
_inherit = 'payment.transaction'
|
||||
|
||||
# --------------------------------------------------
|
||||
# FORM RELATED METHODS
|
||||
# --------------------------------------------------
|
||||
|
||||
def _adyen_form_get_tx_from_data(self, cr, uid, data, context=None):
|
||||
@api.model
|
||||
def _adyen_form_get_tx_from_data(self, data):
|
||||
reference, pspReference = data.get('merchantReference'), data.get('pspReference')
|
||||
if not reference or not pspReference:
|
||||
error_msg = _('Adyen: received data with missing reference (%s) or missing pspReference (%s)') % (reference, pspReference)
|
||||
@@ -183,22 +172,21 @@ class TxAdyen(osv.Model):
|
||||
raise ValidationError(error_msg)
|
||||
|
||||
# find tx -> @TDENOTE use pspReference ?
|
||||
tx_ids = self.pool['payment.transaction'].search(cr, uid, [('reference', '=', reference)], context=context)
|
||||
if not tx_ids or len(tx_ids) > 1:
|
||||
tx = self.env['payment.transaction'].search([('reference', '=', reference)])
|
||||
if not tx or len(tx) > 1:
|
||||
error_msg = _('Adyen: received data for reference %s') % (reference)
|
||||
if not tx_ids:
|
||||
if not tx:
|
||||
error_msg += _('; no order found')
|
||||
else:
|
||||
error_msg += _('; multiple order found')
|
||||
_logger.info(error_msg)
|
||||
raise ValidationError(error_msg)
|
||||
tx = self.pool['payment.transaction'].browse(cr, uid, tx_ids[0], context=context)
|
||||
|
||||
# verify shasign
|
||||
if len(tx.acquirer_id.adyen_skin_hmac_key) == 64:
|
||||
shasign_check = self.pool['payment.acquirer']._adyen_generate_merchant_sig_sha256(tx.acquirer_id, 'out', data)
|
||||
shasign_check = tx.acquirer_id._adyen_generate_merchant_sig_sha256('out', data)
|
||||
else:
|
||||
shasign_check = self.pool['payment.acquirer']._adyen_generate_merchant_sig(tx.acquirer_id, 'out', data)
|
||||
shasign_check = tx.acquirer_id._adyen_generate_merchant_sig('out', data)
|
||||
if shasign_check != data.get('merchantSig'):
|
||||
error_msg = _('Adyen: invalid merchantSig, received %s, computed %s') % (data.get('merchantSig'), shasign_check)
|
||||
_logger.warning(error_msg)
|
||||
@@ -206,27 +194,25 @@ class TxAdyen(osv.Model):
|
||||
|
||||
return tx
|
||||
|
||||
def _adyen_form_get_invalid_parameters(self, cr, uid, ids, data, context=None):
|
||||
tx = self.browse(cr, uid, ids, context=context)[0]
|
||||
def _adyen_form_get_invalid_parameters(self, data):
|
||||
invalid_parameters = []
|
||||
|
||||
# reference at acquirer: pspReference
|
||||
if tx.acquirer_reference and data.get('pspReference') != tx.acquirer_reference:
|
||||
invalid_parameters.append(('pspReference', data.get('pspReference'), tx.acquirer_reference))
|
||||
if self.acquirer_reference and data.get('pspReference') != self.acquirer_reference:
|
||||
invalid_parameters.append(('pspReference', data.get('pspReference'), self.acquirer_reference))
|
||||
# seller
|
||||
if data.get('skinCode') != tx.acquirer_id.adyen_skin_code:
|
||||
invalid_parameters.append(('skinCode', data.get('skinCode'), tx.acquirer_id.adyen_skin_code))
|
||||
if data.get('skinCode') != self.acquirer_id.adyen_skin_code:
|
||||
invalid_parameters.append(('skinCode', data.get('skinCode'), self.acquirer_id.adyen_skin_code))
|
||||
# result
|
||||
if not data.get('authResult'):
|
||||
invalid_parameters.append(('authResult', data.get('authResult'), 'something'))
|
||||
|
||||
return invalid_parameters
|
||||
|
||||
def _adyen_form_validate(self, cr, uid, ids, data, context=None):
|
||||
tx = self.browse(cr, uid, ids, context=context)[0]
|
||||
def _adyen_form_validate(self, data):
|
||||
status = data.get('authResult', 'PENDING')
|
||||
if status == 'AUTHORISED':
|
||||
tx.write({
|
||||
self.write({
|
||||
'state': 'done',
|
||||
'acquirer_reference': data.get('pspReference'),
|
||||
# 'date_validate': data.get('payment_date', fields.datetime.now()),
|
||||
@@ -234,7 +220,7 @@ class TxAdyen(osv.Model):
|
||||
})
|
||||
return True
|
||||
elif status == 'PENDING':
|
||||
tx.write({
|
||||
self.write({
|
||||
'state': 'pending',
|
||||
'acquirer_reference': data.get('pspReference'),
|
||||
})
|
||||
@@ -242,7 +228,7 @@ class TxAdyen(osv.Model):
|
||||
else:
|
||||
error = _('Adyen: feedback error')
|
||||
_logger.info(error)
|
||||
tx.write({
|
||||
self.write({
|
||||
'state': 'error',
|
||||
'state_message': error
|
||||
})
|
||||
|
||||
@@ -3,21 +3,14 @@
|
||||
from lxml import objectify
|
||||
import urlparse
|
||||
|
||||
from openerp.addons.payment.models.payment_acquirer import ValidationError
|
||||
from openerp.addons.payment.tests.common import PaymentAcquirerCommon
|
||||
from openerp.addons.payment_adyen.controllers.main import AdyenController
|
||||
from openerp.tools import mute_logger
|
||||
from odoo.addons.payment.tests.common import PaymentAcquirerCommon
|
||||
from odoo.addons.payment_adyen.controllers.main import AdyenController
|
||||
|
||||
|
||||
class AdyenCommon(PaymentAcquirerCommon):
|
||||
|
||||
def setUp(self):
|
||||
super(AdyenCommon, self).setUp()
|
||||
cr, uid = self.cr, self.uid
|
||||
self.base_url = self.registry('ir.config_parameter').get_param(cr, uid, 'web.base.url')
|
||||
|
||||
# get the adyen account
|
||||
model, self.adyen_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'payment', 'payment_acquirer_adyen')
|
||||
|
||||
# some CC (always use expiration date 06 / 2016, cvc 737, cid 7373 (amex))
|
||||
self.amex = (('370000000000002', '7373'))
|
||||
@@ -36,40 +29,32 @@ class AdyenCommon(PaymentAcquirerCommon):
|
||||
self.mistercash = (('6703444444444449', None, 'user', 'password'))
|
||||
|
||||
|
||||
class AdyenServer2Server(AdyenCommon):
|
||||
|
||||
def test_00_tx_management(self):
|
||||
cr, uid, context = self.cr, self.uid, {}
|
||||
|
||||
|
||||
class AdyenForm(AdyenCommon):
|
||||
|
||||
def test_10_adyen_form_render(self):
|
||||
cr, uid, context = self.cr, self.uid, {}
|
||||
# be sure not to do stupid things
|
||||
adyen = self.payment_acquirer.browse(self.cr, self.uid, self.adyen_id, None)
|
||||
adyen = self.env.ref('payment.payment_acquirer_adyen')
|
||||
self.assertEqual(adyen.environment, 'test', 'test without test environment')
|
||||
|
||||
# ----------------------------------------
|
||||
# Test: button direct rendering
|
||||
# ----------------------------------------
|
||||
|
||||
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||
form_values = {
|
||||
'merchantAccount': 'OpenERPCOM',
|
||||
'merchantReference': 'test_ref0',
|
||||
'skinCode': 'cbqYWvVL',
|
||||
'paymentAmount': '1',
|
||||
'currencyCode': 'EUR',
|
||||
'resURL': '%s' % urlparse.urljoin(self.base_url, AdyenController._return_url),
|
||||
'resURL': '%s' % urlparse.urljoin(base_url, AdyenController._return_url),
|
||||
}
|
||||
|
||||
# render the button
|
||||
res = self.payment_acquirer.render(
|
||||
cr, uid, self.adyen_id,
|
||||
'test_ref0', 0.01, self.currency_euro_id,
|
||||
res = adyen.render(
|
||||
'test_ref0', 0.01, self.currency_euro.id,
|
||||
partner_id=None,
|
||||
partner_values=self.buyer_values,
|
||||
context=context)
|
||||
partner_values=self.buyer_values)
|
||||
|
||||
# check form result
|
||||
tree = objectify.fromstring(res)
|
||||
@@ -82,19 +67,3 @@ class AdyenForm(AdyenCommon):
|
||||
form_values[form_input.get('name')],
|
||||
'adyen: wrong value for input %s: received %s instead of %s' % (form_input.get('name'), form_input.get('value'), form_values[form_input.get('name')])
|
||||
)
|
||||
|
||||
# @mute_logger('openerp.addons.payment_adyen.models.adyen', 'ValidationError')
|
||||
# def test_20_paypal_form_management(self):
|
||||
# cr, uid, context = self.cr, self.uid, {}
|
||||
# # be sure not to do stupid things
|
||||
# adyen = self.payment_acquirer.browse(self.cr, self.uid, self.adyen_id, None)
|
||||
# self.assertEqual(adyen.env, 'test', 'test without test env')
|
||||
|
||||
# {'authResult': u'AUTHORISED',
|
||||
# 'merchantReference': u'SO014',
|
||||
# 'merchantReturnData': u'return_url=/shop/payment/validate',
|
||||
# 'merchantSig': u'GaLRO8aMHFaQX3gQ5BVP/YETzeA=',
|
||||
# 'paymentMethod': u'visa',
|
||||
# 'pspReference': u'8813859935907337',
|
||||
# 'shopperLocale': u'en_US',
|
||||
# 'skinCode': u'cbqYWvVL'}
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<template id="adyen_acquirer_button">
|
||||
@@ -33,4 +33,4 @@
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
</odoo>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="acquirer_form_adyen" model="ir.ui.view">
|
||||
@@ -19,4 +19,4 @@
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user