import base64 import hashlib import hmac import json import pyDes import re from decimal import Decimal from django.utils.text import gettext_lazy as _ from django.utils import timezone from tpv.exceptions import RedsysValidationException, RedsysPaymentException from tpv.models import PaymentTransaction from tpv.settings import ERROR_CODES from tpv.signals import redsys_payment_accepted def compute_signature(salt, payload, key): """ :param salt: order number (Ds_Order or Ds_Merchant_Order) :param payload: Ds_MerchantParameters :param key: shared secret (aka key) from the Redsys Administration Module :return: """ b64_key = base64.b64decode(key) des3 = pyDes.triple_des( b64_key, mode=pyDes.CBC, IV="\0" * 8, pad="\0", padmode=pyDes.PAD_NORMAL ) pepper = des3.encrypt(str(salt)) payload_hash = hmac.new(pepper, payload.encode(), hashlib.sha256).digest() return base64.b64encode(payload_hash) def compare_signatures(signature_1, signature_2): alphanumeric_characters = re.compile("[^a-zA-Z0-9]") sig1safe = re.sub(alphanumeric_characters, "", signature_1) sig2safe = re.sub(alphanumeric_characters, "", signature_2) return sig1safe == sig2safe def validate_payment_for_transaction( request, transaction: PaymentTransaction ) -> Decimal: """ example_response_data = { 'Ds_MerchantCode': '999008881', 'Ds_Terminal': '001', 'Ds_Order': 'a082bd1cda0b488786f23a62e89107c0', 'Ds_Amount': '13532', 'Ds_Currency': '978', 'Ds_Date': '20/03/2024', 'Ds_Hour': '12:21', 'Ds_SecurePayment': '1', 'Ds_Card_Number': '454881******1156', 'Ds_Card_Country': '724', 'Ds_Response': '0000', 'Ds_MerchantData': '', 'Ds_TransactionType': '0', 'Ds_ConsumerLanguage': '1', 'Ds_AuthorisationCode': '182670', 'Ds_Card_Brand': '1', 'Ds_ProcessedPayMethod': '80', 'Ds_ECI': '05', 'Ds_Response_Description': 'OPERACION AUTORIZADA' } """ data = request.POST merchant_parameters = data.get("Ds_MerchantParameters") if not merchant_parameters: raise RedsysValidationException( _("No se ha recibido ningĂșn valor para Ds_MerchantParameters") ) merchant_params = decode_b64_string(merchant_parameters) result = json.loads(merchant_params) transaction_hex = result.get("Ds_Order") assert transaction_hex == transaction.hash.hex status_code = result.get("Ds_Response") if int(status_code) > 100: reason = ERROR_CODES.get(status_code, _("Error no tipificado")) raise RedsysPaymentException(_(f"No se ha realizado el pago. Motivo: {reason}")) amount = Decimal(result.get("Ds_Amount")) / 100 return amount def validate_expiry_date(expiry_date: str): if len(expiry_date) != 4: return False month, year = int(expiry_date[2:4]), int(expiry_date[:2]) if month < 1 or month > 12: return False current_year = int(str(timezone.now().year)[2:]) current_month = timezone.now().month if year < current_year: return False if year == current_year and month > current_month: return False return True def pay_transaction(transaction: PaymentTransaction, amount_paid: Decimal): transaction.status = PaymentTransaction.StatusChoices.PAID transaction.save() redsys_payment_accepted.send_robust( PaymentTransaction.__class__, hash=transaction.hash, amount=amount_paid, ) def decode_b64_string(value: str): return base64.b64decode(value).decode() def decode_b64_dict(value: str): return json.loads(decode_b64_string(value))