add payment_redsys_preauth
This commit is contained in:
parent
0f2c8c0565
commit
08e34c1426
18 changed files with 1043 additions and 0 deletions
2
payment_redsys_preauth/models/__init__.py
Normal file
2
payment_redsys_preauth/models/__init__.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
from . import payment_provider
|
||||
from . import payment_transaction
|
||||
135
payment_redsys_preauth/models/payment_provider.py
Normal file
135
payment_redsys_preauth/models/payment_provider.py
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
# © 2024 - 2025 criptomart
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import logging
|
||||
import json
|
||||
import pprint
|
||||
import requests
|
||||
import urllib
|
||||
|
||||
from odoo import models, api, exceptions, fields
|
||||
from odoo.addons.payment.models.payment_provider import ValidationError
|
||||
from odoo.tools.float_utils import float_compare
|
||||
from odoo.tools.translate import _
|
||||
import odoo.exceptions
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PaymentProvider(models.Model):
|
||||
_inherit = 'payment.provider'
|
||||
|
||||
@api.depends('code')
|
||||
def _compute_feature_support_fields(self):
|
||||
super()._compute_feature_support_fields()
|
||||
self.update({
|
||||
'support_manual_capture': 'partial',
|
||||
})
|
||||
|
||||
def get_redsys_rest_urls(self, environment):
|
||||
""" Redsys Rest URLs
|
||||
"""
|
||||
if environment == 'prod':
|
||||
return {
|
||||
'redsys_rest_url':
|
||||
'https://sis.redsys.es/sis/rest/trataPeticionREST',
|
||||
}
|
||||
else:
|
||||
# redsys no acepta preautorizaciones en test, pero puede que cambie...
|
||||
return {
|
||||
'redsys_rest_url':
|
||||
'https://sis-t.redsys.es:25443/sis/rest/trataPeticionREST',
|
||||
}
|
||||
|
||||
def _prepare_merchant_parameters(self, tx_values):
|
||||
base_url = self._get_website_url()
|
||||
callback_url = "http://botiga.foodcoopbcn.cat"
|
||||
values = {
|
||||
"Ds_Sermepa_Url": self._redsys_get_api_url(),
|
||||
"Ds_Merchant_Amount": str(int(round(tx_values["amount"] * 100))),
|
||||
"Ds_Merchant_Currency": self.redsys_currency or "978",
|
||||
"Ds_Merchant_Order": (
|
||||
tx_values["reference"] and tx_values["reference"][-12:] or False
|
||||
),
|
||||
"Ds_Merchant_MerchantCode": (
|
||||
self.redsys_merchant_code and self.redsys_merchant_code[:9]
|
||||
),
|
||||
"Ds_Merchant_Terminal": self.redsys_terminal or "1",
|
||||
"Ds_Merchant_TransactionType": (self.redsys_transaction_type or "1"),
|
||||
"Ds_Merchant_Titular": tx_values.get(
|
||||
"billing_partner", self.env.user.partner_id
|
||||
).display_name[:60],
|
||||
"Ds_Merchant_MerchantName": (
|
||||
self.redsys_merchant_name and self.redsys_merchant_name[:25]
|
||||
),
|
||||
"Ds_Merchant_MerchantUrl": (
|
||||
"%s/payment/redsys/return" % (callback_url or base_url)
|
||||
)[:250],
|
||||
"Ds_Merchant_MerchantData": self.redsys_merchant_data or "",
|
||||
"Ds_Merchant_ProductDescription": (
|
||||
self.redsys_merchant_description
|
||||
and self.redsys_merchant_description[:125]
|
||||
),
|
||||
"Ds_Merchant_ConsumerLanguage": (self.redsys_merchant_lang or "001"),
|
||||
"Ds_Merchant_UrlOk": "%s/payment/redsys/result/redsys_result_ok" % base_url,
|
||||
"Ds_Merchant_UrlKo": "%s/payment/redsys/result/redsys_result_ko" % base_url,
|
||||
"Ds_Merchant_Paymethods": self.redsys_pay_method or "T",
|
||||
}
|
||||
return self._url_encode64(json.dumps(values)).decode("utf-8")
|
||||
|
||||
def _prepare_s2s_merchant_parameters(self, tx_values):
|
||||
values = {
|
||||
'Ds_Merchant_Amount': str(int(round(tx_values['amount'] * 100))),
|
||||
'Ds_Merchant_Currency': self.redsys_currency or '978',
|
||||
'Ds_Merchant_Order': (
|
||||
tx_values['reference'] and tx_values['reference'][-12:] or
|
||||
False),
|
||||
'Ds_Merchant_MerchantCode': (
|
||||
self.redsys_merchant_code and
|
||||
self.redsys_merchant_code[:9]),
|
||||
'Ds_Merchant_Terminal': self.redsys_terminal or '1',
|
||||
}
|
||||
if 'transaction_type' in tx_values:
|
||||
values.update({'Ds_Merchant_TransactionType': tx_values['transaction_type']})
|
||||
else:
|
||||
values.update({'Ds_Merchant_TransactionType':
|
||||
(self.redsys_transaction_type or '2')}),
|
||||
return self._url_encode64(json.dumps(values))
|
||||
|
||||
def redsys_s2s_create_transaction(self, values):
|
||||
self.ensure_one()
|
||||
redsys_values = dict()
|
||||
merchant_parameters = self._prepare_s2s_merchant_parameters(values)
|
||||
redsys_values.update({
|
||||
'Ds_SignatureVersion': str(self.redsys_signature_version),
|
||||
'Ds_MerchantParameters': merchant_parameters,
|
||||
'Ds_Signature': self.sign_parameters(
|
||||
self.redsys_secret_key, merchant_parameters),
|
||||
})
|
||||
return redsys_values
|
||||
|
||||
def redsys_send_s2s_msg(self, values):
|
||||
toSend = self.redsys_s2s_create_transaction(values)
|
||||
url = self.get_redsys_rest_urls('prod')['redsys_rest_url']
|
||||
_logger.info('Redsys: sending rest values:\n%s', pprint.pformat(toSend))
|
||||
try:
|
||||
if toSend and url:
|
||||
response = requests.post(url, data=toSend)
|
||||
else:
|
||||
raise exceptions.ValidationError('Error en los parámetros de la petición rest')
|
||||
except requests.exceptions.RequestException as e:
|
||||
_logger.error('Redsys: Error sending rest transaction %s:\n%s', values['reference'], str(e))
|
||||
return False
|
||||
if response and response.status_code != 200:
|
||||
_logger.error('Redsys: Error sending rest transaction %s: %s', values['reference'], response.status_code)
|
||||
return False
|
||||
_logger.info('Redsys: rest response:\n%s', pprint.pformat(response.text))
|
||||
try:
|
||||
errorCheck = response.json()
|
||||
if errorCheck.get('errorCode', ''):
|
||||
_logger.error('Redsys: Error recibido tx %s: %s', values['reference'], errorCheck.get('errorCode'))
|
||||
except json.JSONDecodeError:
|
||||
_logger.error('Error al decodificar la respuesta JSON para la transacción %s', values['reference'])
|
||||
return False
|
||||
return errorCheck
|
||||
|
||||
150
payment_redsys_preauth/models/payment_transaction.py
Normal file
150
payment_redsys_preauth/models/payment_transaction.py
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
# © 2024 - 2025 criptomart
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
import logging
|
||||
import json
|
||||
import pprint
|
||||
import requests
|
||||
|
||||
from odoo import models, api, exceptions, fields
|
||||
from odoo.addons.payment.models.payment_provider import ValidationError
|
||||
from odoo.tools.float_utils import float_compare
|
||||
from odoo.tools.translate import _
|
||||
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TxRedsysPreAuth(models.Model):
|
||||
_inherit = 'payment.transaction'
|
||||
|
||||
def _get_specific_rendering_values(self, processing_values):
|
||||
if self.provider_code != "redsys":
|
||||
return super()._get_specific_rendering_values(processing_values)
|
||||
redsys_values = dict(processing_values)
|
||||
merchant_parameters = self.provider_id._prepare_merchant_parameters(
|
||||
processing_values
|
||||
)
|
||||
redsys_values.update(
|
||||
{
|
||||
"api_url": self.provider_id._redsys_get_api_url(),
|
||||
"Ds_SignatureVersion": str(self.provider_id.redsys_signature_version),
|
||||
"Ds_MerchantParameters": merchant_parameters,
|
||||
"Ds_Signature": self.provider_id.sign_parameters(
|
||||
self.provider_id.redsys_secret_key, merchant_parameters
|
||||
),
|
||||
}
|
||||
)
|
||||
return redsys_values
|
||||
|
||||
# chequea que el usuario cancele el pago 9915. cambia el stado de done a authorized
|
||||
@api.model
|
||||
def _get_redsys_state(self, status_code):
|
||||
if 0 <= status_code <= 100:
|
||||
return "authorized"
|
||||
elif status_code <= 203:
|
||||
return "pending"
|
||||
elif status_code == 400:
|
||||
return "cancel"
|
||||
elif status_code == 900:
|
||||
return "done"
|
||||
elif 912 <= status_code <= 9915:
|
||||
return "cancel"
|
||||
else:
|
||||
return "error"
|
||||
|
||||
def _process_notification_data(self, data):
|
||||
if self.provider_code != "redsys":
|
||||
return super()._process_notification_data(data)
|
||||
params = self.merchant_params_json2dict(data)
|
||||
status_code = int(params.get('Ds_Response', '29999'))
|
||||
state = self._get_redsys_state(status_code)
|
||||
vals = {
|
||||
'state': state,
|
||||
'redsys_txnid': params.get('Ds_AuthorisationCode'),
|
||||
}
|
||||
state_message = ""
|
||||
feedback_error = False
|
||||
if state == 'done':
|
||||
vals['state_message'] = _('Ok: %s') % params.get('Ds_Response')
|
||||
self._set_done()
|
||||
self._finalize_post_processing()
|
||||
if state == 'authorized':
|
||||
vals['state_message'] = _('Auth Ok: %s') % params.get('Ds_Response')
|
||||
self._set_authorized()
|
||||
self._finalize_post_processing()
|
||||
elif state == 'pending': # 'Payment error: code: %s.'
|
||||
state_message = _("Redsys: feedback error: %s" %(status_code))
|
||||
self._set_pending()
|
||||
elif state == 'cancel': # 'Payment error: bank unavailable.'
|
||||
state_message = _("Redsys: feedback error: %s" %(status_code))
|
||||
self._set_canceled()
|
||||
else:
|
||||
state_message = _("Redsys: feedback error: %s" %(status_code))
|
||||
self._set_error(state_message)
|
||||
feedback_error = True
|
||||
if state_message:
|
||||
vals['state_message'] = state_message % {
|
||||
"status_code": params.get("Ds_Response"),
|
||||
"error_code": params.get("Ds_ErrorCode"),
|
||||
}
|
||||
if state == 'error':
|
||||
_logger.warning(vals['state_message'])
|
||||
self.write(vals)
|
||||
|
||||
def _send_capture_request(self, amount_to_capture=None):
|
||||
self.ensure_one()
|
||||
self._ensure_provider_is_not_disabled()
|
||||
_logger.info('Redsys: Capturing transaction %s -- %s', self.reference, amount_to_capture)
|
||||
try:
|
||||
if amount_to_capture:
|
||||
values = {'amount': amount_to_capture}
|
||||
else:
|
||||
values = {'amount': sum(order.invoiced_amount for order in self.sale_order_ids)}
|
||||
values.update({'reference': self.reference})
|
||||
values.update({'transaction_type': '2'})
|
||||
response = self.provider_id.redsys_send_s2s_msg(values)
|
||||
if not response:
|
||||
raise exceptions.ValidationError('Error mandando mensaje rest');
|
||||
if response.get('errorCode', ''):
|
||||
raise exceptions.ValidationError('Ref %s: Error Code %s' %(self.reference, response.get('errorCode')))
|
||||
params = self.merchant_params_json2dict(response)
|
||||
_logger.info('Redsys: parameters received:\n%s', pprint.pformat(params))
|
||||
if params['Ds_Response'] == '0900' and params['Ds_Order'] == self.reference:
|
||||
self.provider_reference = params['Ds_AuthorisationCode']
|
||||
self._set_done()
|
||||
_logger.info('Confirmacion de preautorizacion realizada con exito para la transaccion %s', self.reference)
|
||||
message = ('Preautorizacion confirmada')
|
||||
self.sale_order_ids.message_post(body=message)
|
||||
else:
|
||||
raise exceptions.ValidationError('Error en confirmacion de preautorizacion tx: %s - %s', self.reference, params['Ds_Response'])
|
||||
except Exception as e:
|
||||
_logger.exception('Redsys: Error capturando preautorización transacción %s:\n%s', self.reference, str(e))
|
||||
raise exceptions.UserError(_('Error capturing preauthorization for transaction %s:\n%s') % (self.reference, str(e)))
|
||||
|
||||
def _send_void_request(self, amount_to_void=None):
|
||||
self.ensure_one()
|
||||
self._ensure_provider_is_not_disabled()
|
||||
_logger.info('Redsys: Preauthorization cancel for transaction %s', self.reference)
|
||||
try:
|
||||
if amount_to_void:
|
||||
values = {'amount': amount_to_void}
|
||||
else:
|
||||
values = {'amount': self.amount}
|
||||
values.update({'reference': self.reference})
|
||||
values.update({'transaction_type': '9'})
|
||||
response = self.provider_id.redsys_send_s2s_msg(values)
|
||||
if not response:
|
||||
raise exceptions.ValidationError(_('error redsys_s2s_msg'))
|
||||
if response.get('errorCode', ''):
|
||||
raise exceptions.ValidationError('Ref %s: Error Code %s' % (self.reference, response.get('errorCode')))
|
||||
params = self.merchant_params_json2dict(response)
|
||||
_logger.info('Redsys: parameters received):\n%s', pprint.pformat(params))
|
||||
if params['Ds_Response'] == '0400' and params['Ds_Order'] == self.reference:
|
||||
self._set_canceled()
|
||||
_logger.info('Preautorización cancelada con éxito para la transacción %s', self.reference)
|
||||
else:
|
||||
raise exceptions.ValidationError('No se pudo cancelar la preautorización para la transacción %s - %s', self.reference, params['Ds_Response'])
|
||||
except Exception as e:
|
||||
_logger.exception('Redsys Error cancelando preautorización tx %s:\n%s', self.reference, str(e))
|
||||
raise exceptions.UserError(_('Error cancelling preauthorization for transaction %s:\n%s') % (self.reference, str(e)))
|
||||
Loading…
Add table
Add a link
Reference in a new issue