import base64 import json from decimal import Decimal 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 rest_framework import status from rest_framework.test import APITestCase 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(APITestCase, 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 == status.HTTP_200_OK 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 == status.HTTP_409_CONFLICT 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')