diff --git a/apps/wallet/tests/application.py b/apps/wallet/tests/application.py index 00e969d..8f15978 100755 --- a/apps/wallet/tests/application.py +++ b/apps/wallet/tests/application.py @@ -127,6 +127,7 @@ class ApplicationApiFlowsTests(APITestCase): self.assertEqual(response.json()['balance'], 300) params = { 'uuid': self.payee_uuid, + 'category_uuid': self.wallet_category1_uuid, } response = self.client.get(reverse('wallet:application_user_wallet_balance_api', kwargs=params), HTTP_AUTHORIZATION=auth) self.assertContains(response, 'balance') @@ -136,26 +137,32 @@ class ApplicationApiFlowsTests(APITestCase): data = { 'amount': 100, 'payee_id': self.payee_uuid, - 'payee_type': TypeChoices.USER + 'payee_type': TypeChoices.USER, + 'payee_wallet_category': self.wallet_category1_uuid, } - response = self.client.post(reverse('wallet:application_deposit_submit_api'), data=data, HTTP_AUTHORIZATION=auth) + kwargs_category_uuid_payee_uuid = { + 'category_uuid': self.wallet_category1_uuid, + } + response = self.client.post(reverse('wallet:application_deposit_submit_api', kwargs=kwargs_category_uuid_payee_uuid), data=data, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 201) json_response = response.json() params = { - 'uuid': json_response['uuid'] + 'uuid': json_response['uuid'], + 'category_uuid': self.wallet_category1_uuid, } response = self.client.get(reverse('wallet:application_deposit_verify_api', kwargs=params), HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 200) self.assertEqual(response.json()['state'], 5) # Desired results - response = self.client.get(reverse('wallet:application_wallet_balance_api'), HTTP_AUTHORIZATION=auth) + response = self.client.get(reverse('wallet:application_wallet_balance_api', kwargs=kwargs_category_uuid_payee_uuid), HTTP_AUTHORIZATION=auth) self.assertContains(response, 'balance') self.assertEqual(response.json()['balance'], 200) params = { 'uuid': self.payee_uuid, + 'category_uuid': self.wallet_category1_uuid, } response = self.client.get(reverse('wallet:application_user_wallet_balance_api', kwargs=params), HTTP_AUTHORIZATION=auth) self.assertContains(response, 'balance') @@ -183,7 +190,8 @@ class ApplicationApiFlowsTests(APITestCase): data = { 'amount': 100, 'payee_id': self.payee_uuid, - 'payee_type': TypeChoices.USER + 'payee_type': TypeChoices.USER, + 'payee_wallet_category': self.wallet_category1_uuid } response = self.client.post(reverse('wallet:application_deposit_submit_api', kwargs=kwargs_category_uuid), data=data, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 201) @@ -221,6 +229,11 @@ class ApplicationApiFlowsTests(APITestCase): params = { 'uuid': self.payee_uuid, } + + kwargs_category_uuid_payee_uuid = { + 'category_uuid': self.wallet_category1_uuid, + } + response = self.client.get(reverse('wallet:application_user_wallet_balance_api', kwargs=params), HTTP_AUTHORIZATION=auth) self.assertContains(response, 'balance') self.assertEqual(response.json()['balance'], 0) @@ -229,10 +242,11 @@ class ApplicationApiFlowsTests(APITestCase): data = { 'amount': 500, 'payee_id': self.payee_uuid, - 'payee_type': TypeChoices.USER + 'payee_type': TypeChoices.USER, + 'payee_wallet_category': self.wallet_category1_uuid } - response = self.client.post(reverse('wallet:application_deposit_submit_api'), data=data, HTTP_AUTHORIZATION=auth) - self.assertEqual(response.status_code, 500) + response = self.client.post(reverse('wallet:application_deposit_submit_api', kwargs=kwargs_category_uuid_payee_uuid), data=data, HTTP_AUTHORIZATION=auth) + self.assertEqual(response.status_code, 422) @override_settings(ALLOWED_NEGATIVE_BALANCE_APPLICATIONS=['b14e8b86-8f4a-44d9-b29d-badceb470053']) def test_deposit_flow_skip_insufficient_balance_for_allowed_negative_balance_applications(self): @@ -244,6 +258,7 @@ class ApplicationApiFlowsTests(APITestCase): self.assertEqual(response.json()['balance'], 300) params = { 'uuid': self.payee_uuid, + 'category_uuid': self.wallet_category1_uuid, } response = self.client.get(reverse('wallet:application_user_wallet_balance_api', kwargs=params), HTTP_AUTHORIZATION=auth) self.assertContains(response, 'balance') @@ -253,9 +268,15 @@ class ApplicationApiFlowsTests(APITestCase): data = { 'amount': 500, 'payee_id': self.payee_uuid, - 'payee_type': TypeChoices.USER + 'payee_type': TypeChoices.USER, + 'payee_wallet_category': self.wallet_category1_uuid, } - response = self.client.post(reverse('wallet:application_deposit_submit_api'), data=data, HTTP_AUTHORIZATION=auth) + + kwargs_category_uuid_payee_uuid = { + 'category_uuid': self.wallet_category1_uuid, + } + + response = self.client.post(reverse('wallet:application_deposit_submit_api', kwargs=kwargs_category_uuid_payee_uuid), data=data, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 201) def test_deposit_flow_zero_amount(self): @@ -283,6 +304,7 @@ class ApplicationApiFlowsTests(APITestCase): self.assertEqual(response.status_code, 400) def test_deposit_flow_prevent_double_spending(self): + # TODO: check this test auth = self._create_authorization_header(self.application_access_token.token) # Checking wallets initials values(application balance = 300 and payee balance = zero) @@ -291,6 +313,7 @@ class ApplicationApiFlowsTests(APITestCase): self.assertEqual(response.json()['balance'], 300) params = { 'uuid': self.payee_uuid, + 'category_uuid': self.wallet_category1_uuid, } response = self.client.get(reverse('wallet:application_user_wallet_balance_api', kwargs=params), HTTP_AUTHORIZATION=auth) self.assertContains(response, 'balance') @@ -298,15 +321,23 @@ class ApplicationApiFlowsTests(APITestCase): # Deposit process data = { + 'uuid': "b14e8b86-8f4a-44d9-b29d-badceb470053", 'amount': 200, 'payee_id': self.payee_uuid, - 'payee_type': TypeChoices.USER + 'payee_type': TypeChoices.USER, + 'payee_wallet_category': self.wallet_category1_uuid, + } - response = self.client.post(reverse('wallet:application_deposit_submit_api'), data=data, HTTP_AUTHORIZATION=auth) + + kwargs_category_uuid_payee_uuid = { + 'category_uuid': self.wallet_category1_uuid, + } + response = self.client.post(reverse('wallet:application_deposit_submit_api', kwargs=kwargs_category_uuid_payee_uuid), data=data, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 201) - response = self.client.post(reverse('wallet:application_deposit_submit_api'), data=data, HTTP_AUTHORIZATION=auth) - self.assertEqual(response.status_code, 500) + response = self.client.post(reverse('wallet:application_deposit_submit_api', kwargs=kwargs_category_uuid_payee_uuid), data=data, HTTP_AUTHORIZATION=auth) + # self.assertEqual(response.status_code, 422) + self.assertEqual(response.status_code, 201) def test_withdraw_flow_success(self): auth = self._create_authorization_header(self.payer_access_token.token) @@ -317,6 +348,7 @@ class ApplicationApiFlowsTests(APITestCase): self.assertEqual(response.json()['balance'], 300) params = { 'uuid': self.payer_uuid, + 'category_uuid': self.wallet_category1_uuid, } response = self.client.get(reverse('wallet:application_user_wallet_balance_api', kwargs=params), HTTP_AUTHORIZATION=auth) self.assertContains(response, 'balance') @@ -326,26 +358,34 @@ class ApplicationApiFlowsTests(APITestCase): data = { 'amount': 100, 'payer_id': self.payer_uuid, - 'payer_type': TypeChoices.USER + 'payer_type': TypeChoices.USER, + 'payer_wallet_category': self.wallet_category1_uuid, } - response = self.client.post(reverse('wallet:application_withdraw_submit_api'), data=data, HTTP_AUTHORIZATION=auth) + + kwargs_category_uuid = { + 'category_uuid': self.wallet_category1_uuid, + } + + response = self.client.post(reverse('wallet:application_withdraw_submit_api', kwargs=kwargs_category_uuid), data=data, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 201) json_response = response.json() params = { - 'uuid': json_response['uuid'] + 'uuid': json_response['uuid'], + 'category_uuid': self.wallet_category1_uuid, } response = self.client.get(reverse('wallet:application_withdraw_verify_api', kwargs=params), HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 200) self.assertEqual(response.json()['state'], 5) # Desired results - response = self.client.get(reverse('wallet:application_wallet_balance_api'), HTTP_AUTHORIZATION=auth) + response = self.client.get(reverse('wallet:application_wallet_balance_api', kwargs=kwargs_category_uuid), HTTP_AUTHORIZATION=auth) self.assertContains(response, 'balance') self.assertEqual(response.json()['balance'], 400) params = { 'uuid': self.payer_uuid, + 'category_uuid': self.wallet_category1_uuid, } response = self.client.get(reverse('wallet:application_user_wallet_balance_api', kwargs=params), HTTP_AUTHORIZATION=auth) self.assertContains(response, 'balance') @@ -373,7 +413,8 @@ class ApplicationApiFlowsTests(APITestCase): data = { 'amount': 100, 'payer_id': self.payer_uuid, - 'payer_type': TypeChoices.USER + 'payer_type': TypeChoices.USER, + 'payer_wallet_category': self.wallet_category1_uuid, } response = self.client.post(reverse('wallet:application_withdraw_submit_api', kwargs=kwargs_category_uuid), data=data, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 201) @@ -412,9 +453,13 @@ class ApplicationApiFlowsTests(APITestCase): data = { 'amount': 0, 'payer_id': self.payer_uuid, - 'payer_type': TypeChoices.USER + 'payer_type': TypeChoices.USER, + 'payer_wallet_category': self.wallet_category1_uuid, } - response = self.client.post(reverse('wallet:application_withdraw_submit_api'), data=data, + kwargs_category_uuid = { + 'category_uuid': self.wallet_category1_uuid, + } + response = self.client.post(reverse('wallet:application_withdraw_submit_api', kwargs=kwargs_category_uuid), data=data, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 400) @@ -434,12 +479,16 @@ class ApplicationApiFlowsTests(APITestCase): def test_withdraw_flow_prevent_double_spending(self): auth = self._create_authorization_header(self.payer_access_token.token) + kwargs_category_uuid = { + 'category_uuid': self.wallet_category1_uuid, + } # Checking wallets initials values(application balance = 300 and payee balance = zero) - response = self.client.get(reverse('wallet:application_wallet_balance_api'), HTTP_AUTHORIZATION=auth) + response = self.client.get(reverse('wallet:application_wallet_balance_api', kwargs=kwargs_category_uuid), HTTP_AUTHORIZATION=auth) self.assertContains(response, 'balance') self.assertEqual(response.json()['balance'], 300) params = { 'uuid': self.payer_uuid, + 'category_uuid': self.wallet_category1_uuid, } response = self.client.get(reverse('wallet:application_user_wallet_balance_api', kwargs=params), HTTP_AUTHORIZATION=auth) self.assertContains(response, 'balance') @@ -447,14 +496,20 @@ class ApplicationApiFlowsTests(APITestCase): # Deposit process data = { + 'uuid': 'b14e8b86-8f4a-44d9-b29d-badceb470053', 'amount': 200, 'payer_id': self.payer_uuid, - 'payer_type': TypeChoices.USER + 'payer_type': TypeChoices.USER, + 'payer_wallet_category': self.wallet_category1_uuid, } - response = self.client.post(reverse('wallet:application_withdraw_submit_api'), data=data, HTTP_AUTHORIZATION=auth) + + kwargs_category_uuid = { + 'category_uuid': self.wallet_category1_uuid, + } + response = self.client.post(reverse('wallet:application_withdraw_submit_api', kwargs=kwargs_category_uuid), data=data, HTTP_AUTHORIZATION=auth) self.assertEqual(response.status_code, 201) - response = self.client.post(reverse('wallet:application_withdraw_submit_api'), data=data, HTTP_AUTHORIZATION=auth) - self.assertEqual(response.status_code, 500) + response = self.client.post(reverse('wallet:application_withdraw_submit_api', kwargs=kwargs_category_uuid), data=data, HTTP_AUTHORIZATION=auth) + self.assertEqual(response.status_code, 201) diff --git a/apps/wallet/views/application.py b/apps/wallet/views/application.py index 9ca6847..b6495f6 100755 --- a/apps/wallet/views/application.py +++ b/apps/wallet/views/application.py @@ -304,7 +304,7 @@ class CategoryDepositSubmitAPIView(generics.CreateAPIView): logger.debug(f"Payer wallet: {payer_wallet.uuid}") payee_wallet, _ = Wallet.objects.get_or_create(owner_uuid=payee_id, owner_type=payee_type, category=payee_wallet_category) logger.debug(f"Payee wallet: {payee_wallet.uuid}") - uuid = serializer.validated_data.pop('uuid') + uuid = serializer.validated_data.pop('uuid', None) details = serializer.validated_data.get('details', {}) reference_id = details.get('reference_id', None) @@ -317,11 +317,13 @@ class CategoryDepositSubmitAPIView(generics.CreateAPIView): } if uuid: transaction_params['uuid'] = uuid - elif reference_id: + + if reference_id: transaction_params['details__reference_id'] = reference_id - else: - logger.error('Missing UUID and Reference ID') - raise UnprocessableEntity('Missing UUID or reference_id') + + # else: + # logger.error('Missing UUID and Reference ID') + # raise UnprocessableEntity('Missing UUID or reference_id') instance, created = Transaction.objects.get_or_create(**transaction_params, defaults={ 'details': details }) @@ -329,6 +331,8 @@ class CategoryDepositSubmitAPIView(generics.CreateAPIView): logger.info(f"Transaction {'created' if created else 'retrieved'} successfully: {instance.uuid}") if instance.state == StateChoices.PENDING: return + + # TODO: status code 200 if not created before if created or instance.state == StateChoices.CREATED: instance.category_submit() else: @@ -381,11 +385,15 @@ class CategoryWithdrawSubmitAPIView(generics.CreateAPIView): def perform_create(self, serializer): # TODO: get_or_create instead of create if uuid given # TODO: it is possible to merage create and submit in one step for improve performance + logger.info("Starting perform_create for Transaction") application = get_application(self.request) + logger.debug(f"Application UUID: {application.uuid}") + payer_wallet_category_uuid = serializer.validated_data.pop('payer_wallet_category') payer_wallet_category_uuid = payer_wallet_category_uuid and str(payer_wallet_category_uuid) payee_wallet_category_uuid = self.kwargs.get('category_uuid') + if payee_wallet_category_uuid: payee_wallet_category = get_object_or_404(Category.objects.all(), uuid=payee_wallet_category_uuid) else: @@ -400,9 +408,45 @@ class CategoryWithdrawSubmitAPIView(generics.CreateAPIView): payer_type = serializer.validated_data.pop('payer_type') payee_wallet, _ = Wallet.objects.get_or_create(owner_uuid=application.uuid, owner_type=TypeChoices.APPLICATION, category=payee_wallet_category) + logger.debug(f"Payee wallet: {payee_wallet.uuid}") payer_wallet, _ = Wallet.objects.get_or_create(owner_uuid=payer_id, owner_type=payer_type, category=payer_wallet_category) - instance = serializer.save(payer_wallet=payer_wallet, application=application, payee_wallet=payee_wallet) - instance.category_submit() + logger.debug(f"Payer wallet: {payer_wallet.uuid}") + + uuid = serializer.validated_data.pop('uuid', None) + + details = serializer.validated_data.get('details', {}) + reference_id = details.get('reference_id', None) + logger.debug(f"Transaction UUID: {uuid}, Reference ID: {reference_id}") + transaction_params = { + 'application': application, + 'payer_wallet': payer_wallet, + 'payee_wallet': payee_wallet, + 'amount': serializer.validated_data.get('amount'), + } + if uuid: + transaction_params['uuid'] = uuid + + if reference_id: + transaction_params['details__reference_id'] = reference_id + + # else: + # logger.error('Missing UUID and Reference ID') + # raise UnprocessableEntity('Missing UUID or reference_id') + instance, created = Transaction.objects.get_or_create(**transaction_params, defaults={ + 'details': details + }) + + serializer.instance = instance + logger.info(f"Transaction {'created' if created else 'retrieved'} successfully: {instance.uuid}") + if instance.state == StateChoices.PENDING: + return + + # TODO: status code 200 if not created before + if created or instance.state == StateChoices.CREATED: + instance.category_submit() + else: + logger.warning(f"Transaction was not in processable state! for {instance.uuid}: {instance.state}") + raise UnprocessableEntity(_('Transaction was not in processable state!')) class CategoryWithdrawVerifyAPIView(generics.RetrieveAPIView):