From 03357d05e75ed2de49bac118b28d600ab73fd0fc Mon Sep 17 00:00:00 2001 From: sergio-incaser Date: Wed, 5 Nov 2014 19:05:29 +0100 Subject: [PATCH 1/7] [ADD] Nuevo modulo para pagos con tajeta (sermepa-redsys) --- payment_redsys/__init__.py | 23 ++ payment_redsys/__openerp__.py | 19 ++ payment_redsys/controllers/__init__.py | 3 + payment_redsys/controllers/main.py | 34 +++ payment_redsys/data/redsys.xml | 17 ++ payment_redsys/models/__init__.py | 3 + payment_redsys/models/redsys.py | 239 ++++++++++++++++++ payment_redsys/static/description/icon.png | Bin 0 -> 10570 bytes payment_redsys/static/src/img/redsys_icon.png | Bin 0 -> 10570 bytes payment_redsys/tests/__init__.py | 7 + payment_redsys/tests/test_ogone.py | 225 +++++++++++++++++ payment_redsys/views/payment_acquirer.xml | 47 ++++ payment_redsys/views/redsys.xml | 33 +++ 13 files changed, 650 insertions(+) create mode 100644 payment_redsys/__init__.py create mode 100644 payment_redsys/__openerp__.py create mode 100644 payment_redsys/controllers/__init__.py create mode 100644 payment_redsys/controllers/main.py create mode 100644 payment_redsys/data/redsys.xml create mode 100644 payment_redsys/models/__init__.py create mode 100644 payment_redsys/models/redsys.py create mode 100644 payment_redsys/static/description/icon.png create mode 100644 payment_redsys/static/src/img/redsys_icon.png create mode 100644 payment_redsys/tests/__init__.py create mode 100644 payment_redsys/tests/test_ogone.py create mode 100644 payment_redsys/views/payment_acquirer.xml create mode 100644 payment_redsys/views/redsys.xml diff --git a/payment_redsys/__init__.py b/payment_redsys/__init__.py new file mode 100644 index 0000000000..dde6f25c72 --- /dev/null +++ b/payment_redsys/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2013-Today OpenERP SA (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +import models +import controllers diff --git a/payment_redsys/__openerp__.py b/payment_redsys/__openerp__.py new file mode 100644 index 0000000000..e9e8f31b81 --- /dev/null +++ b/payment_redsys/__openerp__.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- + +{ + 'name': 'Redsys Payment Acquirer', + 'category': 'Hidden', + 'summary': 'Payment Acquirer: Redsys Implementation', + 'version': '1.0', + 'description': """ + This modulo add redsys payment gateway + """, + 'author': 'Incaser Informatica S.L.', + 'depends': ['payment'], + 'data': [ + 'views/redsys.xml', + 'views/payment_acquirer.xml', + 'data/redsys.xml', + ], + 'installable': True, +} diff --git a/payment_redsys/controllers/__init__.py b/payment_redsys/controllers/__init__.py new file mode 100644 index 0000000000..bbd183e955 --- /dev/null +++ b/payment_redsys/controllers/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +import main diff --git a/payment_redsys/controllers/main.py b/payment_redsys/controllers/main.py new file mode 100644 index 0000000000..ab6a3e4d2b --- /dev/null +++ b/payment_redsys/controllers/main.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +import logging +import pprint +import werkzeug + +from openerp import http, SUPERUSER_ID +from openerp.http import request + +_logger = logging.getLogger(__name__) + + +class RedsysController(http.Controller): + _return_url = '/payment/redsys/return' + _cancel_url = '/payment/redsys/cancel' + _exception_url = '/payment/redsys/error' + _reject_url = '/payment/redsys/reject' + + @http.route([ + '/payment/redsys/return', + '/payment/redsys/cancel', + '/payment/redsys/error', + '/payment/redsys/reject', + ], type='http', auth='none') + def redsys_return(self, **post): + """ Redsys.""" + _logger.info('Redsys: entering form_feedback with post data %s', + pprint.pformat(post)) # debug + request.registry['payment.transaction'].form_feedback( + request.cr, SUPERUSER_ID, post, 'redsys', + context=request.context) + return_url = post.pop('return_url', '') + if not return_url: + return_url = 'http://localhost:8069/page/gracias-por-su-compra' + return werkzeug.utils.redirect(return_url) diff --git a/payment_redsys/data/redsys.xml b/payment_redsys/data/redsys.xml new file mode 100644 index 0000000000..52fcbc6ce0 --- /dev/null +++ b/payment_redsys/data/redsys.xml @@ -0,0 +1,17 @@ + + + + + + Credit Card Redsys + redsys + + + test + You will be redirected to the redsys website after cliking on the payment button.

]]> +
+
+ +
+
diff --git a/payment_redsys/models/__init__.py b/payment_redsys/models/__init__.py new file mode 100644 index 0000000000..a2313a5454 --- /dev/null +++ b/payment_redsys/models/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +import redsys diff --git a/payment_redsys/models/redsys.py b/payment_redsys/models/redsys.py new file mode 100644 index 0000000000..e8919e52ba --- /dev/null +++ b/payment_redsys/models/redsys.py @@ -0,0 +1,239 @@ +# -*- coding: utf-8 -*- +from hashlib import sha1 +import logging +import urlparse + +from openerp import models, fields, api, _ +from openerp.addons.payment.models.payment_acquirer import ValidationError +from openerp.tools.float_utils import float_compare + +_logger = logging.getLogger(__name__) + + +class AcquirerRedsys(models.Model): + _inherit = 'payment.acquirer' + + def _get_redsys_urls(self, environment): + """ Redsys URLs + """ + if environment == 'prod': + return { + 'redsys_form_url': + 'https://sis.redsys.es/sis/realizarPago/', + } + else: + return { + 'redsys_form_url': + 'https://sis-t.redsys.es:25443/sis/realizarPago/', + } + + @api.model + def _get_providers(self): + providers = super(AcquirerRedsys, self)._get_providers() + providers.append(['redsys', 'Redsys']) + return providers + + redsys_merchant_url = fields.Char('Merchant URL', + required_if_provider='redsys') + redsys_merchant_name = fields.Char('Merchant Name', + required_if_provider='redsys') + redsys_merchant_titular = fields.Char('Merchant Titular', + required_if_provider='redsys') + redsys_merchant_code = fields.Char('Merchant code', + required_if_provider='redsys') + redsys_merchant_description = fields.Char('Product Description', + required_if_provider='redsys') + redsys_secret_key = fields.Char('Secret Key', + required_if_provider='redsys') + redsys_terminal = fields.Char('Terminal', default = '1', + required_if_provider='redsys') + redsys_currency = fields.Char('Currency', default = '978', + required_if_provider='redsys') + redsys_transaction_type = fields.Char('Transtaction Type', default = '0', + required_if_provider='redsys') + redsys_merchant_data = fields.Char('Merchant Data') + redsys_merchant_lang = fields.Selection([('001', 'Castellano'), + ('004', 'Frances'), + ],'Merchant Consumer Language') + redsys_pay_method = fields.Selection([('T', 'Pago con Tarjeta'), + ('R', 'Pago por Transferencia'), + ('D', 'Domiciliacion'), + ],'Payment Method', + default = 'T') + redsys_url_ok = fields.Char('URL OK') + redsys_url_ko = fields.Char('URL KO') + + + def _redsys_generate_digital_sign(self, acquirer, 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' (encoding) or 'out' (decoding). + :param dict values: transaction values + + :return string: shasign + """ + assert acquirer.provider == 'redsys' + + def get_value(key): + if values.get(key): + return values[key] + return '' + + if inout == 'out': + keys = ['Ds_Amount', + 'Ds_Order', + 'Ds_MerchantCode', + 'Ds_Currency', + 'Ds_Response'] + else: + keys = ['Ds_Merchant_Amount', + 'Ds_Merchant_Order', + 'Ds_Merchant_MerchantCode', + 'Ds_Merchant_Currency', + 'Ds_Merchant_TransactionType', + 'Ds_Merchant_MerchantURL'] + sign = ''.join('%s' % (get_value(k)) for k in keys) + #Add the pre-shared secret key at the end of the signature + sign = sign + acquirer.redsys_secret_key + if isinstance(sign, str): + sign = urlparse.parse_qsl(sign) + shasign = sha1(sign).hexdigest().upper() + return shasign + + + @api.model + def redsys_form_generate_values(self, id, partner_values, tx_values): + base_url = self.env['ir.config_parameter'].get_param('web.base.url') + acquirer = self.browse(id) + redsys_tx_values = dict(tx_values) + redsys_tx_values.update({ + 'Ds_Sermepa_Url': ( + self._get_redsys_urls(acquirer.environment)['redsys_form_url']), + 'Ds_Merchant_Amount': int(tx_values['amount'] * 100), + 'Ds_Merchant_Currency': acquirer.redsys_currency or '978', + 'Ds_Merchant_Order': tx_values['reference'][:12], + 'Ds_Merchant_MerchantCode': acquirer.redsys_merchant_code[:9], + 'Ds_Merchant_Terminal': acquirer.redsys_terminal or '1', + 'Ds_Merchant_TransactionType': ( + acquirer.redsys_transaction_type or '0'), + 'Ds_Merchant_Titular': acquirer.redsys_merchant_titular[:60], + 'Ds_Merchant_MerchantName': acquirer.redsys_merchant_name[:25], + 'Ds_Merchant_MerchantURL': (acquirer.redsys_merchant_url + and acquirer.redsys_merchant_url[:250] or ''), + 'Ds_Merchant_MerchantData': acquirer.redsys_merchant_data or '', + 'Ds_Merchant_ProductDescription': ( + acquirer.redsys_merchant_description[:125]), + 'Ds_Merchant_ConsumerLanguage': ( + acquirer.redsys_merchant_lang or '001'), + 'Ds_Merchant_UrlOK': acquirer.redsys_url_ok or '', + 'Ds_Merchant_UrlKO': acquirer.redsys_url_ko or '', + 'Ds_Merchant_PayMethods': acquirer.redsys_pay_method or 'T', + }) + + redsys_tx_values['Ds_Merchant_MerchantSignature'] = ( + self._redsys_generate_digital_sign( + acquirer, 'in', redsys_tx_values)) + return partner_values, redsys_tx_values + + @api.multi + def redsys_get_form_action_url(self): + return self._get_redsys_urls(self.environment)['redsys_form_url'] + +class TxRedsys(models.Model): + _inherit = 'payment.transaction' + + redsys_txnid = fields.Char('Transaction ID') + + # -------------------------------------------------- + # FORM RELATED METHODS + # -------------------------------------------------- + + @api.model + def _redsys_form_get_tx_from_data(self, data): + """ Given a data dict coming from redsys, verify it and + find the related transaction record. """ + reference = data.get('Ds_Order') + pay_id = data.get('Ds_AuthorisationCode') + shasign = data.get('Ds_Signature') + if not reference or not pay_id or not shasign: + error_msg = 'Redsys: received data with missing reference' \ + ' (%s) or pay_id (%s) or shashign (%s)' % (reference, + pay_id, shasign) + _logger.error(error_msg) + raise ValidationError(error_msg) + + tx = self.search([('reference', '=', reference)]) + if not tx or len(tx) > 1: + error_msg = 'Redsys: received data for reference %s' % (reference) + if not tx: + error_msg += '; no order found' + else: + error_msg += '; multiple order found' + _logger.error(error_msg) + raise ValidationError(error_msg) + + #verify shasign + acquirer = self.env['payment.acquirer'] + shasign_check = acquirer._redsys_generate_digital_sign( + tx.acquirer_id, 'out', data) + if shasign_check.upper() != shasign.upper(): + error_msg = 'Redsys: invalid shasign, received\ %s, computed %s,'\ + ' for data %s' % (shasign, shasign_check, data) + _logger.error(error_msg) + raise ValidationError(error_msg) + return tx + + @api.model + def _redsys_form_get_invalid_parameters(self, tx, data): + invalid_parameters = [] + + if (tx.acquirer_reference + and data.get('Ds_Order')) != tx.acquirer_reference: + invalid_parameters.append( + ('Transaction Id', data.get('Ds_Order'), + tx.acquirer_reference)) + # check what is buyed + if float_compare(float(data.get('Ds_Amount', '0.0'))/100, + tx.amount, 2) != 0: + invalid_parameters.append('Amount', data.get('Ds_Amount'), + '%.2f' % tx.amount) + return invalid_parameters + + @api.model + def _redsys_form_validate(self, tx, data): + status_code = int(data.get('Ds_Response','29999')) + if (status_code >= 0) and (status_code <= 99): + tx.write({ + 'state': 'done', + 'redsys_txnid': data.get('Ds_AuthorisationCode'), + 'state_message': _('Ok: %s') % data.get('Ds_Response'), + }) + return True + if (status_code >= 101) and (status_code <= 202): + # 'Payment error: code: %s.' + tx.write({ + 'state': 'pending', + 'redsys_txnid': data.get('Ds_AuthorisationCode'), + 'state_message': _('Error: %s') % data.get('Ds_Response'), + }) + return True + if (status_code == 912) and (status_code == 9912): + # 'Payment error: bank unavailable.' + tx.write({ + 'state': 'cancel', + 'redsys_txnid': data.get('Ds_AuthorisationCode'), + 'state_message': _('Bank Error: %s') + % data.get('Ds_Response'), + }) + return True + else: + error = 'Redsys: feedback error' + _logger.info(error) + tx.write({ + 'state': 'error', + 'redsys_txnid': data.get('Ds_AuthorisationCode'), + 'state_message': error, + }) + return False diff --git a/payment_redsys/static/description/icon.png b/payment_redsys/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..932b73691553e7f575dda8eed7e96c755be9e456 GIT binary patch literal 10570 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 YA250&OU>;7RR91007*qoM6N<$g7j!9IsgCw literal 0 HcmV?d00001 diff --git a/payment_redsys/static/src/img/redsys_icon.png b/payment_redsys/static/src/img/redsys_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..932b73691553e7f575dda8eed7e96c755be9e456 GIT binary patch literal 10570 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 YA250&OU>;7RR91007*qoM6N<$g7j!9IsgCw literal 0 HcmV?d00001 diff --git a/payment_redsys/tests/__init__.py b/payment_redsys/tests/__init__.py new file mode 100644 index 0000000000..5b92012191 --- /dev/null +++ b/payment_redsys/tests/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- + +# from . import test_ogone + +checks = [ + # test_ogone, +] diff --git a/payment_redsys/tests/test_ogone.py b/payment_redsys/tests/test_ogone.py new file mode 100644 index 0000000000..0db68214bd --- /dev/null +++ b/payment_redsys/tests/test_ogone.py @@ -0,0 +1,225 @@ +# -*- coding: utf-8 -*- + +from lxml import objectify +import time +import urlparse + +from openerp.addons.payment.models.payment_acquirer import ValidationError +from openerp.addons.payment.tests.common import PaymentAcquirerCommon +from openerp.addons.payment_ogone.controllers.main import OgoneController +from openerp.tools import mute_logger + + +class OgonePayment(PaymentAcquirerCommon): + + def setUp(self): + super(OgonePayment, 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.ogone_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'payment_ogone', 'payment_acquirer_ogone') + + def test_10_ogone_form_render(self): + cr, uid, context = self.cr, self.uid, {} + # be sure not to do stupid thing + ogone = self.payment_acquirer.browse(self.cr, self.uid, self.ogone_id, None) + self.assertEqual(ogone.environment, 'test', 'test without test environment') + + # ---------------------------------------- + # Test: button direct rendering + shasign + # ---------------------------------------- + + form_values = { + 'PSPID': 'dummy', + 'ORDERID': 'test_ref0', + 'AMOUNT': '1', + 'CURRENCY': 'EUR', + 'LANGUAGE': 'en_US', + 'CN': 'Norbert Buyer', + 'EMAIL': 'norbert.buyer@example.com', + 'OWNERZIP': '1000', + 'OWNERADDRESS': 'Huge Street 2/543', + 'OWNERCTY': 'Belgium', + 'OWNERTOWN': 'Sin City', + 'OWNERTELNO': '0032 12 34 56 78', + 'SHASIGN': '815f67b8ff70d234ffcf437c13a9fa7f807044cc', + 'ACCEPTURL': '%s' % urlparse.urljoin(self.base_url, OgoneController._accept_url), + 'DECLINEURL': '%s' % urlparse.urljoin(self.base_url, OgoneController._decline_url), + 'EXCEPTIONURL': '%s' % urlparse.urljoin(self.base_url, OgoneController._exception_url), + 'CANCELURL': '%s' % urlparse.urljoin(self.base_url, OgoneController._cancel_url), + } + + # render the button + res = self.payment_acquirer.render( + cr, uid, self.ogone_id, + 'test_ref0', 0.01, self.currency_euro_id, + partner_id=None, + partner_values=self.buyer_values, + context=context) + + # check form result + tree = objectify.fromstring(res) + self.assertEqual(tree.get('action'), 'https://secure.ogone.com/ncol/test/orderstandard.asp', 'ogone: wrong form POST url') + for form_input in tree.input: + if form_input.get('name') in ['submit']: + continue + self.assertEqual( + form_input.get('value'), + form_values[form_input.get('name')], + 'ogone: wrong value for input %s: received %s instead of %s' % (form_input.get('name'), form_input.get('value'), form_values[form_input.get('name')]) + ) + + # ---------------------------------------- + # Test2: button using tx + validation + # ---------------------------------------- + + # create a new draft tx + tx_id = self.payment_transaction.create( + cr, uid, { + 'amount': 0.01, + 'acquirer_id': self.ogone_id, + 'currency_id': self.currency_euro_id, + 'reference': 'test_ref0', + 'partner_id': self.buyer_id, + }, context=context + ) + # render the button + res = self.payment_acquirer.render( + cr, uid, self.ogone_id, + 'should_be_erased', 0.01, self.currency_euro, + tx_id=tx_id, + partner_id=None, + partner_values=self.buyer_values, + context=context) + + # check form result + tree = objectify.fromstring(res) + self.assertEqual(tree.get('action'), 'https://secure.ogone.com/ncol/test/orderstandard.asp', 'ogone: wrong form POST url') + for form_input in tree.input: + if form_input.get('name') in ['submit']: + continue + self.assertEqual( + form_input.get('value'), + form_values[form_input.get('name')], + 'ogone: wrong value for form 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_ogone.models.ogone', 'ValidationError') + def test_20_ogone_form_management(self): + cr, uid, context = self.cr, self.uid, {} + # be sure not to do stupid thing + ogone = self.payment_acquirer.browse(self.cr, self.uid, self.ogone_id, None) + self.assertEqual(ogone.environment, 'test', 'test without test environment') + + # typical data posted by ogone after client has successfully paid + ogone_post_data = { + 'orderID': u'test_ref_2', + 'STATUS': u'9', + 'CARDNO': u'XXXXXXXXXXXX0002', + 'PAYID': u'25381582', + 'CN': u'Norbert Buyer', + 'NCERROR': u'0', + 'TRXDATE': u'11/15/13', + 'IP': u'85.201.233.72', + 'BRAND': u'VISA', + 'ACCEPTANCE': u'test123', + 'currency': u'EUR', + 'amount': u'1.95', + 'SHASIGN': u'7B7B0ED9CBC4A85543A9073374589033A62A05A5', + 'ED': u'0315', + 'PM': u'CreditCard' + } + + # should raise error about unknown tx + with self.assertRaises(ValidationError): + self.payment_transaction.ogone_form_feedback(cr, uid, ogone_post_data, context=context) + + # create tx + tx_id = self.payment_transaction.create( + cr, uid, { + 'amount': 1.95, + 'acquirer_id': self.ogone_id, + 'currency_id': self.currency_euro_id, + 'reference': 'test_ref_2', + 'partner_name': 'Norbert Buyer', + 'partner_country_id': self.country_france_id, + }, context=context + ) + # validate it + self.payment_transaction.ogone_form_feedback(cr, uid, ogone_post_data, context=context) + # check state + tx = self.payment_transaction.browse(cr, uid, tx_id, context=context) + self.assertEqual(tx.state, 'done', 'ogone: validation did not put tx into done state') + self.assertEqual(tx.ogone_payid, ogone_post_data.get('PAYID'), 'ogone: validation did not update tx payid') + + # reset tx + tx.write({'state': 'draft', 'date_validate': False, 'ogone_payid': False}) + + # now ogone post is ok: try to modify the SHASIGN + ogone_post_data['SHASIGN'] = 'a4c16bae286317b82edb49188d3399249a784691' + with self.assertRaises(ValidationError): + self.payment_transaction.ogone_form_feedback(cr, uid, ogone_post_data, context=context) + + # simulate an error + ogone_post_data['STATUS'] = 2 + ogone_post_data['SHASIGN'] = 'a4c16bae286317b82edb49188d3399249a784691' + self.payment_transaction.ogone_form_feedback(cr, uid, ogone_post_data, context=context) + # check state + tx = self.payment_transaction.browse(cr, uid, tx_id, context=context) + self.assertEqual(tx.state, 'error', 'ogone: erroneous validation did not put tx into error state') + + def test_30_ogone_s2s(self): + test_ref = 'test_ref_%.15f' % time.time() + cr, uid, context = self.cr, self.uid, {} + # be sure not to do stupid thing + ogone = self.payment_acquirer.browse(self.cr, self.uid, self.ogone_id, None) + self.assertEqual(ogone.environment, 'test', 'test without test environment') + + # create a new draft tx + tx_id = self.payment_transaction.create( + cr, uid, { + 'amount': 0.01, + 'acquirer_id': self.ogone_id, + 'currency_id': self.currency_euro_id, + 'reference': test_ref, + 'partner_id': self.buyer_id, + 'type': 'server2server', + }, context=context + ) + + # create an alias + res = self.payment_transaction.ogone_s2s_create_alias( + cr, uid, tx_id, { + 'expiry_date_mm': '01', + 'expiry_date_yy': '2015', + 'holder_name': 'Norbert Poilu', + 'number': '4000000000000002', + 'brand': 'VISA', + }, context=context) + + # check an alias is set, containing at least OPENERP + tx = self.payment_transaction.browse(cr, uid, tx_id, context=context) + self.assertIn('OPENERP', tx.partner_reference, 'ogone: wrong partner reference after creating an alias') + + res = self.payment_transaction.ogone_s2s_execute(cr, uid, tx_id, {}, context=context) + # print res + + +# { +# 'orderID': u'reference', +# 'STATUS': u'9', +# 'CARDNO': u'XXXXXXXXXXXX0002', +# 'PAYID': u'24998692', +# 'CN': u'Norbert Poilu', +# 'NCERROR': u'0', +# 'TRXDATE': u'11/05/13', +# 'IP': u'85.201.233.72', +# 'BRAND': u'VISA', +# 'ACCEPTANCE': u'test123', +# 'currency': u'EUR', +# 'amount': u'1.95', +# 'SHASIGN': u'EFDC56879EF7DE72CCF4B397076B5C9A844CB0FA', +# 'ED': u'0314', +# 'PM': u'CreditCard' +# } diff --git a/payment_redsys/views/payment_acquirer.xml b/payment_redsys/views/payment_acquirer.xml new file mode 100644 index 0000000000..7b7f916c2c --- /dev/null +++ b/payment_redsys/views/payment_acquirer.xml @@ -0,0 +1,47 @@ + + + + + + acquirer.form.redsys + payment.acquirer + + + + + + + + + + + + + + + + + + + + + + + + + acquirer.transaction.form.redsys + payment.transaction + + + + + + + + + + + + + + diff --git a/payment_redsys/views/redsys.xml b/payment_redsys/views/redsys.xml new file mode 100644 index 0000000000..621a0e996b --- /dev/null +++ b/payment_redsys/views/redsys.xml @@ -0,0 +1,33 @@ + + + + + + + + From 70b8762ba0ae295953521c60e90913107d5c9156 Mon Sep 17 00:00:00 2001 From: sergio-incaser Date: Wed, 5 Nov 2014 22:03:55 +0100 Subject: [PATCH 2/7] [FIX] PEP8 --- payment_redsys/__openerp__.py | 8 +-- payment_redsys/controllers/main.py | 4 +- payment_redsys/models/redsys.py | 96 +++++++++++++++--------------- 3 files changed, 54 insertions(+), 54 deletions(-) diff --git a/payment_redsys/__openerp__.py b/payment_redsys/__openerp__.py index e9e8f31b81..993686d936 100644 --- a/payment_redsys/__openerp__.py +++ b/payment_redsys/__openerp__.py @@ -6,14 +6,14 @@ 'summary': 'Payment Acquirer: Redsys Implementation', 'version': '1.0', 'description': """ - This modulo add redsys payment gateway + This modulo add redsys payment gateway """, 'author': 'Incaser Informatica S.L.', 'depends': ['payment'], 'data': [ - 'views/redsys.xml', - 'views/payment_acquirer.xml', - 'data/redsys.xml', + 'views/redsys.xml', + 'views/payment_acquirer.xml', + 'data/redsys.xml', ], 'installable': True, } diff --git a/payment_redsys/controllers/main.py b/payment_redsys/controllers/main.py index ab6a3e4d2b..58bee0eff8 100644 --- a/payment_redsys/controllers/main.py +++ b/payment_redsys/controllers/main.py @@ -26,8 +26,8 @@ def redsys_return(self, **post): _logger.info('Redsys: entering form_feedback with post data %s', pprint.pformat(post)) # debug request.registry['payment.transaction'].form_feedback( - request.cr, SUPERUSER_ID, post, 'redsys', - context=request.context) + request.cr, SUPERUSER_ID, post, 'redsys', + context=request.context) return_url = post.pop('return_url', '') if not return_url: return_url = 'http://localhost:8069/page/gracias-por-su-compra' diff --git a/payment_redsys/models/redsys.py b/payment_redsys/models/redsys.py index e8919e52ba..061c1abcbd 100644 --- a/payment_redsys/models/redsys.py +++ b/payment_redsys/models/redsys.py @@ -18,12 +18,12 @@ def _get_redsys_urls(self, environment): """ if environment == 'prod': return { - 'redsys_form_url': + 'redsys_form_url': 'https://sis.redsys.es/sis/realizarPago/', } else: return { - 'redsys_form_url': + 'redsys_form_url': 'https://sis-t.redsys.es:25443/sis/realizarPago/', } @@ -33,40 +33,38 @@ def _get_providers(self): providers.append(['redsys', 'Redsys']) return providers - redsys_merchant_url = fields.Char('Merchant URL', - required_if_provider='redsys') - redsys_merchant_name = fields.Char('Merchant Name', - required_if_provider='redsys') - redsys_merchant_titular = fields.Char('Merchant Titular', - required_if_provider='redsys') - redsys_merchant_code = fields.Char('Merchant code', - required_if_provider='redsys') - redsys_merchant_description = fields.Char('Product Description', - required_if_provider='redsys') - redsys_secret_key = fields.Char('Secret Key', - required_if_provider='redsys') - redsys_terminal = fields.Char('Terminal', default = '1', - required_if_provider='redsys') - redsys_currency = fields.Char('Currency', default = '978', - required_if_provider='redsys') - redsys_transaction_type = fields.Char('Transtaction Type', default = '0', + redsys_merchant_url = fields.Char('Merchant URL', required_if_provider='redsys') + redsys_merchant_name = fields.Char('Merchant Name', + required_if_provider='redsys') + redsys_merchant_titular = fields.Char('Merchant Titular', + required_if_provider='redsys') + redsys_merchant_code = fields.Char('Merchant code', + required_if_provider='redsys') + redsys_merchant_description = fields.Char('Product Description', + required_if_provider='redsys') + redsys_secret_key = fields.Char('Secret Key', + required_if_provider='redsys') + redsys_terminal = fields.Char('Terminal', default='1', + required_if_provider='redsys') + redsys_currency = fields.Char('Currency', default='978', + required_if_provider='redsys') + redsys_transaction_type = fields.Char('Transtaction Type', default='0', + required_if_provider='redsys') redsys_merchant_data = fields.Char('Merchant Data') redsys_merchant_lang = fields.Selection([('001', 'Castellano'), ('004', 'Frances'), - ],'Merchant Consumer Language') + ], 'Merchant Consumer Language') redsys_pay_method = fields.Selection([('T', 'Pago con Tarjeta'), - ('R', 'Pago por Transferencia'), - ('D', 'Domiciliacion'), - ],'Payment Method', - default = 'T') + ('R', 'Pago por Transferencia'), + ('D', 'Domiciliacion'), + ], 'Payment Method', + default='T') redsys_url_ok = fields.Char('URL OK') redsys_url_ko = fields.Char('URL KO') - def _redsys_generate_digital_sign(self, acquirer, 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' (encoding) or 'out' (decoding). @@ -95,22 +93,22 @@ def get_value(key): 'Ds_Merchant_TransactionType', 'Ds_Merchant_MerchantURL'] sign = ''.join('%s' % (get_value(k)) for k in keys) - #Add the pre-shared secret key at the end of the signature + # Add the pre-shared secret key at the end of the signature sign = sign + acquirer.redsys_secret_key if isinstance(sign, str): sign = urlparse.parse_qsl(sign) shasign = sha1(sign).hexdigest().upper() return shasign - @api.model def redsys_form_generate_values(self, id, partner_values, tx_values): base_url = self.env['ir.config_parameter'].get_param('web.base.url') acquirer = self.browse(id) redsys_tx_values = dict(tx_values) redsys_tx_values.update({ - 'Ds_Sermepa_Url': ( - self._get_redsys_urls(acquirer.environment)['redsys_form_url']), + 'Ds_Sermepa_Url': + (self._get_redsys_urls(acquirer.environment) + ['redsys_form_url']), 'Ds_Merchant_Amount': int(tx_values['amount'] * 100), 'Ds_Merchant_Currency': acquirer.redsys_currency or '978', 'Ds_Merchant_Order': tx_values['reference'][:12], @@ -120,8 +118,9 @@ def redsys_form_generate_values(self, id, partner_values, tx_values): acquirer.redsys_transaction_type or '0'), 'Ds_Merchant_Titular': acquirer.redsys_merchant_titular[:60], 'Ds_Merchant_MerchantName': acquirer.redsys_merchant_name[:25], - 'Ds_Merchant_MerchantURL': (acquirer.redsys_merchant_url - and acquirer.redsys_merchant_url[:250] or ''), + 'Ds_Merchant_MerchantURL': + (acquirer.redsys_merchant_url + and acquirer.redsys_merchant_url[:250] or ''), 'Ds_Merchant_MerchantData': acquirer.redsys_merchant_data or '', 'Ds_Merchant_ProductDescription': ( acquirer.redsys_merchant_description[:125]), @@ -136,11 +135,12 @@ def redsys_form_generate_values(self, id, partner_values, tx_values): self._redsys_generate_digital_sign( acquirer, 'in', redsys_tx_values)) return partner_values, redsys_tx_values - + @api.multi def redsys_get_form_action_url(self): return self._get_redsys_urls(self.environment)['redsys_form_url'] + class TxRedsys(models.Model): _inherit = 'payment.transaction' @@ -149,10 +149,10 @@ class TxRedsys(models.Model): # -------------------------------------------------- # FORM RELATED METHODS # -------------------------------------------------- - + @api.model def _redsys_form_get_tx_from_data(self, data): - """ Given a data dict coming from redsys, verify it and + """ Given a data dict coming from redsys, verify it and find the related transaction record. """ reference = data.get('Ds_Order') pay_id = data.get('Ds_AuthorisationCode') @@ -174,36 +174,36 @@ def _redsys_form_get_tx_from_data(self, data): _logger.error(error_msg) raise ValidationError(error_msg) - #verify shasign + # verify shasign acquirer = self.env['payment.acquirer'] shasign_check = acquirer._redsys_generate_digital_sign( - tx.acquirer_id, 'out', data) + tx.acquirer_id, 'out', data) if shasign_check.upper() != shasign.upper(): error_msg = 'Redsys: invalid shasign, received\ %s, computed %s,'\ ' for data %s' % (shasign, shasign_check, data) _logger.error(error_msg) raise ValidationError(error_msg) return tx - + @api.model def _redsys_form_get_invalid_parameters(self, tx, data): invalid_parameters = [] - if (tx.acquirer_reference - and data.get('Ds_Order')) != tx.acquirer_reference: + if (tx.acquirer_reference + and data.get('Ds_Order')) != tx.acquirer_reference: invalid_parameters.append( - ('Transaction Id', data.get('Ds_Order'), - tx.acquirer_reference)) + ('Transaction Id', data.get('Ds_Order'), + tx.acquirer_reference)) # check what is buyed - if float_compare(float(data.get('Ds_Amount', '0.0'))/100, - tx.amount, 2) != 0: + if (float_compare(float(data.get('Ds_Amount', '0.0'))/100, + tx.amount, 2) != 0): invalid_parameters.append('Amount', data.get('Ds_Amount'), '%.2f' % tx.amount) return invalid_parameters - + @api.model def _redsys_form_validate(self, tx, data): - status_code = int(data.get('Ds_Response','29999')) + status_code = int(data.get('Ds_Response', '29999')) if (status_code >= 0) and (status_code <= 99): tx.write({ 'state': 'done', @@ -224,8 +224,8 @@ def _redsys_form_validate(self, tx, data): tx.write({ 'state': 'cancel', 'redsys_txnid': data.get('Ds_AuthorisationCode'), - 'state_message': _('Bank Error: %s') - % data.get('Ds_Response'), + 'state_message': (_('Bank Error: %s') + % data.get('Ds_Response')), }) return True else: From 6461ac0818837183d449b434dbe2aa4af1c09fbc Mon Sep 17 00:00:00 2001 From: sergio-incaser Date: Wed, 5 Nov 2014 22:12:34 +0100 Subject: [PATCH 3/7] [FIX] delete incorrect files --- payment_redsys/tests/__init__.py | 7 - payment_redsys/tests/test_ogone.py | 225 ----------------------------- 2 files changed, 232 deletions(-) delete mode 100644 payment_redsys/tests/__init__.py delete mode 100644 payment_redsys/tests/test_ogone.py diff --git a/payment_redsys/tests/__init__.py b/payment_redsys/tests/__init__.py deleted file mode 100644 index 5b92012191..0000000000 --- a/payment_redsys/tests/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -# -*- coding: utf-8 -*- - -# from . import test_ogone - -checks = [ - # test_ogone, -] diff --git a/payment_redsys/tests/test_ogone.py b/payment_redsys/tests/test_ogone.py deleted file mode 100644 index 0db68214bd..0000000000 --- a/payment_redsys/tests/test_ogone.py +++ /dev/null @@ -1,225 +0,0 @@ -# -*- coding: utf-8 -*- - -from lxml import objectify -import time -import urlparse - -from openerp.addons.payment.models.payment_acquirer import ValidationError -from openerp.addons.payment.tests.common import PaymentAcquirerCommon -from openerp.addons.payment_ogone.controllers.main import OgoneController -from openerp.tools import mute_logger - - -class OgonePayment(PaymentAcquirerCommon): - - def setUp(self): - super(OgonePayment, 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.ogone_id = self.registry('ir.model.data').get_object_reference(cr, uid, 'payment_ogone', 'payment_acquirer_ogone') - - def test_10_ogone_form_render(self): - cr, uid, context = self.cr, self.uid, {} - # be sure not to do stupid thing - ogone = self.payment_acquirer.browse(self.cr, self.uid, self.ogone_id, None) - self.assertEqual(ogone.environment, 'test', 'test without test environment') - - # ---------------------------------------- - # Test: button direct rendering + shasign - # ---------------------------------------- - - form_values = { - 'PSPID': 'dummy', - 'ORDERID': 'test_ref0', - 'AMOUNT': '1', - 'CURRENCY': 'EUR', - 'LANGUAGE': 'en_US', - 'CN': 'Norbert Buyer', - 'EMAIL': 'norbert.buyer@example.com', - 'OWNERZIP': '1000', - 'OWNERADDRESS': 'Huge Street 2/543', - 'OWNERCTY': 'Belgium', - 'OWNERTOWN': 'Sin City', - 'OWNERTELNO': '0032 12 34 56 78', - 'SHASIGN': '815f67b8ff70d234ffcf437c13a9fa7f807044cc', - 'ACCEPTURL': '%s' % urlparse.urljoin(self.base_url, OgoneController._accept_url), - 'DECLINEURL': '%s' % urlparse.urljoin(self.base_url, OgoneController._decline_url), - 'EXCEPTIONURL': '%s' % urlparse.urljoin(self.base_url, OgoneController._exception_url), - 'CANCELURL': '%s' % urlparse.urljoin(self.base_url, OgoneController._cancel_url), - } - - # render the button - res = self.payment_acquirer.render( - cr, uid, self.ogone_id, - 'test_ref0', 0.01, self.currency_euro_id, - partner_id=None, - partner_values=self.buyer_values, - context=context) - - # check form result - tree = objectify.fromstring(res) - self.assertEqual(tree.get('action'), 'https://secure.ogone.com/ncol/test/orderstandard.asp', 'ogone: wrong form POST url') - for form_input in tree.input: - if form_input.get('name') in ['submit']: - continue - self.assertEqual( - form_input.get('value'), - form_values[form_input.get('name')], - 'ogone: wrong value for input %s: received %s instead of %s' % (form_input.get('name'), form_input.get('value'), form_values[form_input.get('name')]) - ) - - # ---------------------------------------- - # Test2: button using tx + validation - # ---------------------------------------- - - # create a new draft tx - tx_id = self.payment_transaction.create( - cr, uid, { - 'amount': 0.01, - 'acquirer_id': self.ogone_id, - 'currency_id': self.currency_euro_id, - 'reference': 'test_ref0', - 'partner_id': self.buyer_id, - }, context=context - ) - # render the button - res = self.payment_acquirer.render( - cr, uid, self.ogone_id, - 'should_be_erased', 0.01, self.currency_euro, - tx_id=tx_id, - partner_id=None, - partner_values=self.buyer_values, - context=context) - - # check form result - tree = objectify.fromstring(res) - self.assertEqual(tree.get('action'), 'https://secure.ogone.com/ncol/test/orderstandard.asp', 'ogone: wrong form POST url') - for form_input in tree.input: - if form_input.get('name') in ['submit']: - continue - self.assertEqual( - form_input.get('value'), - form_values[form_input.get('name')], - 'ogone: wrong value for form 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_ogone.models.ogone', 'ValidationError') - def test_20_ogone_form_management(self): - cr, uid, context = self.cr, self.uid, {} - # be sure not to do stupid thing - ogone = self.payment_acquirer.browse(self.cr, self.uid, self.ogone_id, None) - self.assertEqual(ogone.environment, 'test', 'test without test environment') - - # typical data posted by ogone after client has successfully paid - ogone_post_data = { - 'orderID': u'test_ref_2', - 'STATUS': u'9', - 'CARDNO': u'XXXXXXXXXXXX0002', - 'PAYID': u'25381582', - 'CN': u'Norbert Buyer', - 'NCERROR': u'0', - 'TRXDATE': u'11/15/13', - 'IP': u'85.201.233.72', - 'BRAND': u'VISA', - 'ACCEPTANCE': u'test123', - 'currency': u'EUR', - 'amount': u'1.95', - 'SHASIGN': u'7B7B0ED9CBC4A85543A9073374589033A62A05A5', - 'ED': u'0315', - 'PM': u'CreditCard' - } - - # should raise error about unknown tx - with self.assertRaises(ValidationError): - self.payment_transaction.ogone_form_feedback(cr, uid, ogone_post_data, context=context) - - # create tx - tx_id = self.payment_transaction.create( - cr, uid, { - 'amount': 1.95, - 'acquirer_id': self.ogone_id, - 'currency_id': self.currency_euro_id, - 'reference': 'test_ref_2', - 'partner_name': 'Norbert Buyer', - 'partner_country_id': self.country_france_id, - }, context=context - ) - # validate it - self.payment_transaction.ogone_form_feedback(cr, uid, ogone_post_data, context=context) - # check state - tx = self.payment_transaction.browse(cr, uid, tx_id, context=context) - self.assertEqual(tx.state, 'done', 'ogone: validation did not put tx into done state') - self.assertEqual(tx.ogone_payid, ogone_post_data.get('PAYID'), 'ogone: validation did not update tx payid') - - # reset tx - tx.write({'state': 'draft', 'date_validate': False, 'ogone_payid': False}) - - # now ogone post is ok: try to modify the SHASIGN - ogone_post_data['SHASIGN'] = 'a4c16bae286317b82edb49188d3399249a784691' - with self.assertRaises(ValidationError): - self.payment_transaction.ogone_form_feedback(cr, uid, ogone_post_data, context=context) - - # simulate an error - ogone_post_data['STATUS'] = 2 - ogone_post_data['SHASIGN'] = 'a4c16bae286317b82edb49188d3399249a784691' - self.payment_transaction.ogone_form_feedback(cr, uid, ogone_post_data, context=context) - # check state - tx = self.payment_transaction.browse(cr, uid, tx_id, context=context) - self.assertEqual(tx.state, 'error', 'ogone: erroneous validation did not put tx into error state') - - def test_30_ogone_s2s(self): - test_ref = 'test_ref_%.15f' % time.time() - cr, uid, context = self.cr, self.uid, {} - # be sure not to do stupid thing - ogone = self.payment_acquirer.browse(self.cr, self.uid, self.ogone_id, None) - self.assertEqual(ogone.environment, 'test', 'test without test environment') - - # create a new draft tx - tx_id = self.payment_transaction.create( - cr, uid, { - 'amount': 0.01, - 'acquirer_id': self.ogone_id, - 'currency_id': self.currency_euro_id, - 'reference': test_ref, - 'partner_id': self.buyer_id, - 'type': 'server2server', - }, context=context - ) - - # create an alias - res = self.payment_transaction.ogone_s2s_create_alias( - cr, uid, tx_id, { - 'expiry_date_mm': '01', - 'expiry_date_yy': '2015', - 'holder_name': 'Norbert Poilu', - 'number': '4000000000000002', - 'brand': 'VISA', - }, context=context) - - # check an alias is set, containing at least OPENERP - tx = self.payment_transaction.browse(cr, uid, tx_id, context=context) - self.assertIn('OPENERP', tx.partner_reference, 'ogone: wrong partner reference after creating an alias') - - res = self.payment_transaction.ogone_s2s_execute(cr, uid, tx_id, {}, context=context) - # print res - - -# { -# 'orderID': u'reference', -# 'STATUS': u'9', -# 'CARDNO': u'XXXXXXXXXXXX0002', -# 'PAYID': u'24998692', -# 'CN': u'Norbert Poilu', -# 'NCERROR': u'0', -# 'TRXDATE': u'11/05/13', -# 'IP': u'85.201.233.72', -# 'BRAND': u'VISA', -# 'ACCEPTANCE': u'test123', -# 'currency': u'EUR', -# 'amount': u'1.95', -# 'SHASIGN': u'EFDC56879EF7DE72CCF4B397076B5C9A844CB0FA', -# 'ED': u'0314', -# 'PM': u'CreditCard' -# } From c1b956085ee49c72a832a5bbe106197104e47799 Mon Sep 17 00:00:00 2001 From: sergio-incaser Date: Wed, 5 Nov 2014 23:40:51 +0100 Subject: [PATCH 4/7] [FIX] Flake8 --- payment_redsys/__init__.py | 4 ++-- payment_redsys/controllers/__init__.py | 2 +- payment_redsys/models/redsys.py | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/payment_redsys/__init__.py b/payment_redsys/__init__.py index dde6f25c72..625eb32b08 100644 --- a/payment_redsys/__init__.py +++ b/payment_redsys/__init__.py @@ -19,5 +19,5 @@ # ############################################################################## -import models -import controllers +from . import models +from . import controllers diff --git a/payment_redsys/controllers/__init__.py b/payment_redsys/controllers/__init__.py index bbd183e955..65a8c12013 100644 --- a/payment_redsys/controllers/__init__.py +++ b/payment_redsys/controllers/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -import main +from . import main diff --git a/payment_redsys/models/redsys.py b/payment_redsys/models/redsys.py index 061c1abcbd..165cfa2c81 100644 --- a/payment_redsys/models/redsys.py +++ b/payment_redsys/models/redsys.py @@ -102,7 +102,6 @@ def get_value(key): @api.model def redsys_form_generate_values(self, id, partner_values, tx_values): - base_url = self.env['ir.config_parameter'].get_param('web.base.url') acquirer = self.browse(id) redsys_tx_values = dict(tx_values) redsys_tx_values.update({ From 5408a2ab550855d2fabac2b2080ec9fdc1b02b30 Mon Sep 17 00:00:00 2001 From: sergio-incaser Date: Fri, 19 Dec 2014 10:10:43 +0100 Subject: [PATCH 5/7] [FIX] payment_redsys Add timestamp in reference. Pedro Baeza recomendations. --- payment_redsys/README.rst | 52 ++++++++++++++++++ payment_redsys/__openerp__.py | 41 +++++++++++++- payment_redsys/controllers/main.py | 11 ++-- payment_redsys/models/__init__.py | 2 +- payment_redsys/models/redsys.py | 19 ++++--- payment_redsys/static/description/icon.png | Bin 10570 -> 8505 bytes payment_redsys/static/src/img/redsys_icon.png | Bin 10570 -> 9137 bytes 7 files changed, 111 insertions(+), 14 deletions(-) create mode 100644 payment_redsys/README.rst diff --git a/payment_redsys/README.rst b/payment_redsys/README.rst new file mode 100644 index 0000000000..1f95369313 --- /dev/null +++ b/payment_redsys/README.rst @@ -0,0 +1,52 @@ +Pasarela de pago Redsys +======================== + + +Este modulo añade la opcion de pago a traves de la pasarela de Redsys + + + +PARAMETROS: + +Nombre del comercio: Indicaremos el nombre del comercio. + +Número de comercio (FUC): Indicaremos el número de comercio que + +nuestra entidad nos ha comunicado. + +Clave secreta de encriptación: Indicaremos la clave de encriptación + +que tiene el comercio. + +Número de terminal: Indicaremos el terminal del TPV. + +Tipo de firma: Seleccionaremos el tipo de firma del comercio. + +Tipo de moneda: Seleccionaremos la moneda de nuestro terminal TPV +(Normalmente Euros). + +Tipo de transacción: Indicaremos el tipo de transacción, 0. + +Idiomas TPV: Indicaremos el idiomas en el TPV. + +URL_OK/URL_KO: durante el proceso del pago, y una vez que +se muestra al cliente la pantalla con el resultado del mismo, es +posible redirigir su navegador a una URL para las transacciones +autorizadas y a otra si la transacción ha sido denegada. A estas +se las denomina URL_OK y URL_KO, respectivamente. Se trata +de dos URL que pueden ser proporcionadas por el comercio. + + + +Requirements +------------ + +Odoo v8.0 + + +Note +---- + +Se tiene que verificar la configuración del comercio en el +módulo de administración de Redsys, donde la opción “Parámetros en las +URLs” debe tener el valor “SI”. diff --git a/payment_redsys/__openerp__.py b/payment_redsys/__openerp__.py index 993686d936..2908e94ac8 100644 --- a/payment_redsys/__openerp__.py +++ b/payment_redsys/__openerp__.py @@ -6,7 +6,46 @@ 'summary': 'Payment Acquirer: Redsys Implementation', 'version': '1.0', 'description': """ - This modulo add redsys payment gateway + +======================== +Pasarela de pago Redsys +======================== + + +Este modulo añade la opcion de pago a traves de la pasarela de Redsys + + + +PARAMETROS: + +Nombre del comercio: Indicaremos el nombre del comercio. + +Número de comercio (FUC): Indicaremos el número de comercio que + +nuestra entidad nos ha comunicado. + +Clave secreta de encriptación: Indicaremos la clave de encriptación + +que tiene el comercio. + +Número de terminal: Indicaremos el terminal del TPV. + +Tipo de firma: Seleccionaremos el tipo de firma del comercio. + +Tipo de moneda: Seleccionaremos la moneda de nuestro terminal TPV +(Normalmente Euros). + +Tipo de transacción: Indicaremos el tipo de transacción, 0. + +Idiomas TPV: Indicaremos el idiomas en el TPV. + +URL_OK/URL_KO: durante el proceso del pago, y una vez que +se muestra al cliente la pantalla con el resultado del mismo, es +posible redirigir su navegador a una URL para las transacciones +autorizadas y a otra si la transacción ha sido denegada. A estas +se las denomina URL_OK y URL_KO, respectivamente. Se trata +de dos URL que pueden ser proporcionadas por el comercio. + """, 'author': 'Incaser Informatica S.L.', 'depends': ['payment'], diff --git a/payment_redsys/controllers/main.py b/payment_redsys/controllers/main.py index 58bee0eff8..6c16bb1b4f 100644 --- a/payment_redsys/controllers/main.py +++ b/payment_redsys/controllers/main.py @@ -24,11 +24,12 @@ class RedsysController(http.Controller): def redsys_return(self, **post): """ Redsys.""" _logger.info('Redsys: entering form_feedback with post data %s', - pprint.pformat(post)) # debug - request.registry['payment.transaction'].form_feedback( - request.cr, SUPERUSER_ID, post, 'redsys', - context=request.context) + pprint.pformat(post)) + if post: + request.registry['payment.transaction'].form_feedback( + request.cr, SUPERUSER_ID, post, 'redsys', + context=request.context) return_url = post.pop('return_url', '') if not return_url: - return_url = 'http://localhost:8069/page/gracias-por-su-compra' + return_url = '/shop' return werkzeug.utils.redirect(return_url) diff --git a/payment_redsys/models/__init__.py b/payment_redsys/models/__init__.py index a2313a5454..06f93b5c38 100644 --- a/payment_redsys/models/__init__.py +++ b/payment_redsys/models/__init__.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -import redsys +from . import redsys diff --git a/payment_redsys/models/redsys.py b/payment_redsys/models/redsys.py index 165cfa2c81..2052f68f42 100644 --- a/payment_redsys/models/redsys.py +++ b/payment_redsys/models/redsys.py @@ -6,7 +6,7 @@ from openerp import models, fields, api, _ from openerp.addons.payment.models.payment_acquirer import ValidationError from openerp.tools.float_utils import float_compare - +from datetime import datetime _logger = logging.getLogger(__name__) @@ -103,6 +103,7 @@ def get_value(key): @api.model def redsys_form_generate_values(self, id, partner_values, tx_values): acquirer = self.browse(id) + tx_values['reference'] += datetime.now().strftime('%M%S') redsys_tx_values = dict(tx_values) redsys_tx_values.update({ 'Ds_Sermepa_Url': @@ -111,17 +112,21 @@ def redsys_form_generate_values(self, id, partner_values, tx_values): 'Ds_Merchant_Amount': int(tx_values['amount'] * 100), 'Ds_Merchant_Currency': acquirer.redsys_currency or '978', 'Ds_Merchant_Order': tx_values['reference'][:12], - 'Ds_Merchant_MerchantCode': acquirer.redsys_merchant_code[:9], + 'Ds_Merchant_MerchantCode': acquirer.redsys_merchant_code and + acquirer.redsys_merchant_code[:9], 'Ds_Merchant_Terminal': acquirer.redsys_terminal or '1', 'Ds_Merchant_TransactionType': ( acquirer.redsys_transaction_type or '0'), - 'Ds_Merchant_Titular': acquirer.redsys_merchant_titular[:60], - 'Ds_Merchant_MerchantName': acquirer.redsys_merchant_name[:25], + 'Ds_Merchant_Titular': acquirer.redsys_merchant_titular[:60] and + acquirer.redsys_merchant_titular[:60], + 'Ds_Merchant_MerchantName': acquirer.redsys_merchant_name and + acquirer.redsys_merchant_name[:25], 'Ds_Merchant_MerchantURL': (acquirer.redsys_merchant_url and acquirer.redsys_merchant_url[:250] or ''), 'Ds_Merchant_MerchantData': acquirer.redsys_merchant_data or '', 'Ds_Merchant_ProductDescription': ( + acquirer.redsys_merchant_description and acquirer.redsys_merchant_description[:125]), 'Ds_Merchant_ConsumerLanguage': ( acquirer.redsys_merchant_lang or '001'), @@ -153,7 +158,7 @@ class TxRedsys(models.Model): def _redsys_form_get_tx_from_data(self, data): """ Given a data dict coming from redsys, verify it and find the related transaction record. """ - reference = data.get('Ds_Order') + reference = data.get('Ds_Order', '')[:-4] pay_id = data.get('Ds_AuthorisationCode') shasign = data.get('Ds_Signature') if not reference or not pay_id or not shasign: @@ -196,8 +201,8 @@ def _redsys_form_get_invalid_parameters(self, tx, data): # check what is buyed if (float_compare(float(data.get('Ds_Amount', '0.0'))/100, tx.amount, 2) != 0): - invalid_parameters.append('Amount', data.get('Ds_Amount'), - '%.2f' % tx.amount) + invalid_parameters.append(('Amount', data.get('Ds_Amount'), + '%.2f' % tx.amount)) return invalid_parameters @api.model diff --git a/payment_redsys/static/description/icon.png b/payment_redsys/static/description/icon.png index 932b73691553e7f575dda8eed7e96c755be9e456..3d7100f0b78729b2854bcad44463cf6ca45d9980 100644 GIT binary patch literal 8505 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 literal 10570 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 YA250&OU>;7RR91007*qoM6N<$g7j!9IsgCw diff --git a/payment_redsys/static/src/img/redsys_icon.png b/payment_redsys/static/src/img/redsys_icon.png index 932b73691553e7f575dda8eed7e96c755be9e456..a61bb7a3888827e77f843903e616e64fa45e6e7a 100644 GIT binary patch literal 9137 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 YA250&OU>;7RR91007*qoM6N<$g7j!9IsgCw From 32f50e27045cf0736d9f891f54a696bec3881584 Mon Sep 17 00:00:00 2001 From: sergio-incaser Date: Fri, 19 Dec 2014 10:31:06 +0100 Subject: [PATCH 6/7] [FIX] payment_redsys Pylint fixes --- payment_redsys/models/redsys.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/payment_redsys/models/redsys.py b/payment_redsys/models/redsys.py index 2052f68f42..9c2e648034 100644 --- a/payment_redsys/models/redsys.py +++ b/payment_redsys/models/redsys.py @@ -183,7 +183,7 @@ def _redsys_form_get_tx_from_data(self, data): shasign_check = acquirer._redsys_generate_digital_sign( tx.acquirer_id, 'out', data) if shasign_check.upper() != shasign.upper(): - error_msg = 'Redsys: invalid shasign, received\ %s, computed %s,'\ + error_msg = 'Redsys: invalid shasign, received\ %s, computed %s,' \ ' for data %s' % (shasign, shasign_check, data) _logger.error(error_msg) raise ValidationError(error_msg) From abba856de8e6bd872fbf8108e4a444692d47ff28 Mon Sep 17 00:00:00 2001 From: sergio-incaser Date: Fri, 19 Dec 2014 10:59:45 +0100 Subject: [PATCH 7/7] [FIX] payment_redsys Pylint fixes --- payment_redsys/README.rst | 4 +--- payment_redsys/__openerp__.py | 42 --------------------------------- payment_redsys/models/redsys.py | 2 +- 3 files changed, 2 insertions(+), 46 deletions(-) diff --git a/payment_redsys/README.rst b/payment_redsys/README.rst index 1f95369313..6079bd3db8 100644 --- a/payment_redsys/README.rst +++ b/payment_redsys/README.rst @@ -1,5 +1,5 @@ Pasarela de pago Redsys -======================== +======================= Este modulo añade la opcion de pago a traves de la pasarela de Redsys @@ -11,11 +11,9 @@ PARAMETROS: Nombre del comercio: Indicaremos el nombre del comercio. Número de comercio (FUC): Indicaremos el número de comercio que - nuestra entidad nos ha comunicado. Clave secreta de encriptación: Indicaremos la clave de encriptación - que tiene el comercio. Número de terminal: Indicaremos el terminal del TPV. diff --git a/payment_redsys/__openerp__.py b/payment_redsys/__openerp__.py index 2908e94ac8..d5505d355e 100644 --- a/payment_redsys/__openerp__.py +++ b/payment_redsys/__openerp__.py @@ -5,48 +5,6 @@ 'category': 'Hidden', 'summary': 'Payment Acquirer: Redsys Implementation', 'version': '1.0', - 'description': """ - -======================== -Pasarela de pago Redsys -======================== - - -Este modulo añade la opcion de pago a traves de la pasarela de Redsys - - - -PARAMETROS: - -Nombre del comercio: Indicaremos el nombre del comercio. - -Número de comercio (FUC): Indicaremos el número de comercio que - -nuestra entidad nos ha comunicado. - -Clave secreta de encriptación: Indicaremos la clave de encriptación - -que tiene el comercio. - -Número de terminal: Indicaremos el terminal del TPV. - -Tipo de firma: Seleccionaremos el tipo de firma del comercio. - -Tipo de moneda: Seleccionaremos la moneda de nuestro terminal TPV -(Normalmente Euros). - -Tipo de transacción: Indicaremos el tipo de transacción, 0. - -Idiomas TPV: Indicaremos el idiomas en el TPV. - -URL_OK/URL_KO: durante el proceso del pago, y una vez que -se muestra al cliente la pantalla con el resultado del mismo, es -posible redirigir su navegador a una URL para las transacciones -autorizadas y a otra si la transacción ha sido denegada. A estas -se las denomina URL_OK y URL_KO, respectivamente. Se trata -de dos URL que pueden ser proporcionadas por el comercio. - - """, 'author': 'Incaser Informatica S.L.', 'depends': ['payment'], 'data': [ diff --git a/payment_redsys/models/redsys.py b/payment_redsys/models/redsys.py index 9c2e648034..0a58e93a75 100644 --- a/payment_redsys/models/redsys.py +++ b/payment_redsys/models/redsys.py @@ -183,7 +183,7 @@ def _redsys_form_get_tx_from_data(self, data): shasign_check = acquirer._redsys_generate_digital_sign( tx.acquirer_id, 'out', data) if shasign_check.upper() != shasign.upper(): - error_msg = 'Redsys: invalid shasign, received\ %s, computed %s,' \ + error_msg = 'Redsys: invalid shasign, received %s, computed %s,' \ ' for data %s' % (shasign, shasign_check, data) _logger.error(error_msg) raise ValidationError(error_msg)