import base64 import json from decimal import Decimal from django.test import TestCase from unittest.mock import patch import pytest from django.contrib.auth import get_user_model from django.urls import reverse from django.utils import timezone from config.tests.mixins import Response from shop.exceptions import RedsysPaymentException from shop.models import CustomerAddress, Order, ShopSettings, Tax from shop.redsys import RedsysClient from shop.tests.mixins import CreateProductsMixin from shop.utils import create_order, create_order_line_for_product, validate_expiry_date User = get_user_model() def redsys_response_ok(*args, **kwargs): response = { 'Ds_SignatureVersion': 'HMAC_SHA256_V1', 'Ds_MerchantParameters': 'eyJEc19BbW91bnQiOiIxNDUiLCJEc19DdXJyZW5jeSI6Ijk3OCIsIkRzX09yZGVyIjoiMTQ0NjA2ODU4MSIsIkRzX01lcmNoYW50Q29kZSI6Ijk5OTAwODg4MSIsIkRzX1Rlcm1pbmFsIjoiMSIsIkRzX1Jlc3BvbnNlIjoiMDAwMCIsIkRzX0F1dGhvcmlzYXRpb25Db2RlIjoiNTAxNjAyIiwiRHNfVHJhbnNhY3Rpb25UeXBlIjoiMCIsIkRzX1NlY3VyZVBheW1lbnQiOiIwIiwiRHNfTGFuZ3VhZ2UiOiIxIiwiRHNfQ2FyZE51bWJlciI6IjQ1NDg4MSoqKioqKioqMDQiLCJEc19NZXJjaGFudERhdGEiOiIiLCJEc19DYXJkX0NvdW50cnkiOiI3MjQiLCJEc19DYXJkX0JyYW5kIjoiMSJ9', 'Ds_Signature': 'QVxoXwwp919v7XYjyBjhr1VXozESRosHPb3PDW-rcME=', } return Response(response, status_code=200) def redsys_response_error(*args, **kwargs): response = {'errorCode': 'SIS00001'} return Response(response, status_code=200) class TestRedsysTPV(TestCase, CreateProductsMixin): def setUp(self): self.tax = Tax.objects.create(code='IVA', value=21) self.customer = get_user_model().objects.create_user( username='11111111H', first_name='Darth', last_name='Maul', email='darth@maul.com', password='dathomir' ) self.customer_shipping_address = CustomerAddress.objects.create( user=self.customer, address='Dathomir', address_town='Dathomir', address_zip='00001', address_state='Dathomir', address_phone='900000000', address_type=CustomerAddress.Types.SHIPPING, ) self.customer_billing_address = CustomerAddress.objects.create( user=self.customer, address='Dathomir', address_town='Dathomir', address_zip='00001', address_state='Dathomir', address_phone='900000000', address_type=CustomerAddress.Types.BILLING, ) self.product = self.create_product() self.order = self.create_order() self.order.calculate_total_from_lines() self.settings = ShopSettings.load() self.settings.merchant_code = '999008881' self.settings.shared_secret = 'sq7HjrUOBfKmC576ILgskD5srU870gJ7' # Debug secret self.settings.save() def create_order(self): order = create_order( customer=self.customer, billing_address=self.customer_billing_address.address, billing_city=self.customer_billing_address.address_town, billing_state=self.customer_billing_address.address_state, billing_zip=self.customer_billing_address.address_zip, billing_country=self.customer_billing_address.address_country, ) create_order_line_for_product(self.product, quantity=Decimal('1.0'), order=order) return order def test_redsys_client(self): amount_to_pay = Decimal('12.10') client = RedsysClient() merchant_parameters = client._get_merchant_parameters_for_order(self.order) assert merchant_parameters.get('DS_MERCHANT_ORDER') == self.order.code assert merchant_parameters.get('DS_MERCHANT_AMOUNT') == str(int(amount_to_pay * 100)) assert merchant_parameters.get('DS_MERCHANT_TERMINAL') == self.settings.terminal assert merchant_parameters.get('DS_MERCHANT_MERCHANTCODE') == self.settings.merchant_code client.get_body_for_order(self.order) def test_redsys_webhook(self): redsys_response_data = { 'Ds_MerchantCode': '999008881', 'Ds_Terminal': '001', 'Ds_Order': self.order.code, 'Ds_Amount': str(self.order.total * 100), 'Ds_Currency': '978', 'Ds_Date': '01/01/2024', 'Ds_Hour': '00:00', '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', } redsys_response_data_str = json.dumps(redsys_response_data) b64_merchant_params = base64.b64encode(redsys_response_data_str.encode()).decode() response = self.client.post( reverse('shop:webhook', kwargs={'uuid': self.order.uuid}), data={ 'Ds_MerchantParameters': b64_merchant_params, 'Ds_Signature': self.settings.shared_secret, 'Ds_SignatureVersion': 'HMAC_SHA256_V1', }, ) self.order.refresh_from_db() assert response.status_code == 200 assert self.order.status == Order.Statuses.STATUS_PAID def test_redsys_webhook_payment_error(self): redsys_response_data = { 'Ds_MerchantCode': '999008881', 'Ds_Terminal': '001', 'Ds_Order': self.order.code, 'Ds_Amount': '1000', 'Ds_Currency': '978', 'Ds_Date': '01/01/2024', 'Ds_Hour': '00:00', 'Ds_SecurePayment': '1', 'Ds_Card_Number': '454881******1156', 'Ds_Card_Country': '724', 'Ds_Response': '0184', 'Ds_MerchantData': '', 'Ds_TransactionType': '0', 'Ds_ConsumerLanguage': '1', 'Ds_AuthorisationCode': '182670', 'Ds_Card_Brand': '1', 'Ds_ProcessedPayMethod': '80', 'Ds_ECI': '05', 'Ds_Response_Description': 'ERROR', } redsys_response_data_str = json.dumps(redsys_response_data) b64_merchant_params = base64.b64encode(redsys_response_data_str.encode()).decode() response = self.client.post( reverse('shop:webhook', kwargs={'uuid': self.order.uuid}), data={ 'Ds_MerchantParameters': b64_merchant_params, 'Ds_Signature': self.settings.shared_secret, 'Ds_SignatureVersion': 'HMAC_SHA256_V1', }, ) assert response.status_code == 409 self.order.refresh_from_db() assert self.order.status == Order.Statuses.STATUS_ERROR def test_expiry_date(self): now = timezone.now() previous_year = str(now.year - 1).rjust(2, '0') current_month = str(now.month).rjust(2, '0') current_year = str(now.year)[-2:] assert not validate_expiry_date('042024') assert not validate_expiry_date(f'{previous_year}{current_month}') assert validate_expiry_date(f'{current_year}{current_month}') @patch('requests.post', redsys_response_ok) def test_redsys_rest_client(self): client = RedsysClient() params = client.pay_order_rest(self.order, pan='4548810000000003', expiry_date='122049', cvv2='285') assert params.get('Ds_Order') == '1446068581' assert params.get('Ds_MerchantCode') == self.settings.merchant_code @patch('requests.post', redsys_response_error) def test_redsys_rest_client_error(self): client = RedsysClient() with pytest.raises(RedsysPaymentException): client.pay_order_rest(self.order, pan='4548810000000003', expiry_date='122049', cvv2='285')