Skip to content

Commit

Permalink
Merge pull request #72 from incaser/8.0
Browse files Browse the repository at this point in the history
[ADD] payment_redsys: Nuevo modulo para pagos con tarjeta de credito (sermepa-redsys)
  • Loading branch information
pedrobaeza committed Dec 19, 2014
2 parents 46b0914 + abba856 commit 698874a
Show file tree
Hide file tree
Showing 12 changed files with 470 additions and 0 deletions.
50 changes: 50 additions & 0 deletions payment_redsys/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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”.
23 changes: 23 additions & 0 deletions payment_redsys/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################

from . import models
from . import controllers
16 changes: 16 additions & 0 deletions payment_redsys/__openerp__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-

{
'name': 'Redsys Payment Acquirer',
'category': 'Hidden',
'summary': 'Payment Acquirer: Redsys Implementation',
'version': '1.0',
'author': 'Incaser Informatica S.L.',
'depends': ['payment'],
'data': [
'views/redsys.xml',
'views/payment_acquirer.xml',
'data/redsys.xml',
],
'installable': True,
}
3 changes: 3 additions & 0 deletions payment_redsys/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-

from . import main
35 changes: 35 additions & 0 deletions payment_redsys/controllers/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# -*- 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))
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 = '/shop'
return werkzeug.utils.redirect(return_url)
17 changes: 17 additions & 0 deletions payment_redsys/data/redsys.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data noupdate="1">

<record id="payment_acquirer_redsys" model="payment.acquirer">
<field name="name">Credit Card Redsys</field>
<field name="provider">redsys</field>
<field name="company_id" ref="base.main_company"/>
<field name="view_template_id" ref="redsys_acquirer_button"/>
<field name="environment">test</field>
<field name="pre_msg"><![CDATA[
<p>You will be redirected to the redsys website after cliking on the payment button.</p>]]>
</field>
</record>

</data>
</openerp>
3 changes: 3 additions & 0 deletions payment_redsys/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-

from . import redsys
243 changes: 243 additions & 0 deletions payment_redsys/models/redsys.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
# -*- 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
from datetime import datetime
_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):
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':
(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 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] 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'),
'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', '')[:-4]
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
Binary file added payment_redsys/static/description/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added payment_redsys/static/src/img/redsys_icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 698874a

Please sign in to comment.