diff --git a/saleor/graphql/payment/mutations/transaction/transaction_create.py b/saleor/graphql/payment/mutations/transaction/transaction_create.py index c9de09d630f..ac76be8ac1f 100644 --- a/saleor/graphql/payment/mutations/transaction/transaction_create.py +++ b/saleor/graphql/payment/mutations/transaction/transaction_create.py @@ -136,8 +136,10 @@ def validate_external_url(cls, external_url: Optional[str], error_code: str): @classmethod def validate_metadata_keys( # type: ignore[override] - cls, metadata_list: List[dict], field_name, error_code + cls, metadata_list: Optional[List[dict]], field_name, error_code ): + if not metadata_list: + return if metadata_contains_empty_key(metadata_list): raise ValidationError( { diff --git a/saleor/graphql/payment/tests/mutations/test_transaction_create.py b/saleor/graphql/payment/tests/mutations/test_transaction_create.py index 9c0395174ad..2855acfd153 100644 --- a/saleor/graphql/payment/tests/mutations/test_transaction_create.py +++ b/saleor/graphql/payment/tests/mutations/test_transaction_create.py @@ -195,6 +195,63 @@ def test_transaction_create_for_order_by_app( assert transaction.external_url == external_url +def test_transaction_create_for_order_by_app_metadata_null_value( + order_with_lines, permission_manage_payments, app_api_client +): + # given + name = "Credit Card" + psp_reference = "PSP reference - 123" + available_actions = [ + TransactionActionEnum.CHARGE.name, + TransactionActionEnum.CHARGE.name, + ] + authorized_value = Decimal("10") + external_url = f"http://{TEST_SERVER_DOMAIN}/external-url" + + variables = { + "id": graphene.Node.to_global_id("Order", order_with_lines.pk), + "transaction": { + "name": name, + "pspReference": psp_reference, + "availableActions": available_actions, + "amountAuthorized": { + "amount": authorized_value, + "currency": "USD", + }, + "metadata": None, + "privateMetadata": None, + "externalUrl": external_url, + }, + } + + # when + response = app_api_client.post_graphql( + MUTATION_TRANSACTION_CREATE, variables, permissions=[permission_manage_payments] + ) + + # then + available_actions = list(set(available_actions)) + + transaction = order_with_lines.payment_transactions.first() + content = get_graphql_content(response) + data = content["data"]["transactionCreate"]["transaction"] + assert data["actions"] == available_actions + assert data["pspReference"] == psp_reference + assert data["authorizedAmount"]["amount"] == authorized_value + assert data["externalUrl"] == external_url + assert data["createdBy"]["id"] == to_global_id_or_none(app_api_client.app) + + assert available_actions == list(map(str.upper, transaction.available_actions)) + assert psp_reference == transaction.psp_reference + assert authorized_value == transaction.authorized_value + assert transaction.metadata == {} + assert transaction.private_metadata == {} + assert transaction.app_identifier == app_api_client.app.identifier + assert transaction.app == app_api_client.app + assert transaction.user is None + assert transaction.external_url == external_url + + def test_transaction_create_for_order_updates_order_total_authorized_by_app( order_with_lines, permission_manage_payments, app_api_client ): @@ -351,6 +408,67 @@ def test_transaction_create_for_checkout_by_app( assert transaction.user is None +def test_transaction_create_for_checkout_by_app_metadata_null_value( + checkout_with_items, permission_manage_payments, app_api_client +): + # given + name = "Credit Card" + psp_reference = "PSP reference - 123" + available_actions = [ + TransactionActionEnum.CHARGE.name, + TransactionActionEnum.CHARGE.name, + ] + authorized_value = Decimal("10") + external_url = f"http://{TEST_SERVER_DOMAIN}/external-url" + + variables = { + "id": graphene.Node.to_global_id("Checkout", checkout_with_items.pk), + "transaction": { + "name": name, + "pspReference": psp_reference, + "availableActions": available_actions, + "amountAuthorized": { + "amount": authorized_value, + "currency": "USD", + }, + "metadata": None, + "privateMetadata": None, + "externalUrl": external_url, + }, + } + + # when + response = app_api_client.post_graphql( + MUTATION_TRANSACTION_CREATE, variables, permissions=[permission_manage_payments] + ) + + # then + checkout_with_items.refresh_from_db() + assert checkout_with_items.charge_status == CheckoutChargeStatus.NONE + assert checkout_with_items.authorize_status == CheckoutAuthorizeStatus.PARTIAL + + available_actions = list(set(available_actions)) + + transaction = checkout_with_items.payment_transactions.first() + content = get_graphql_content(response) + data = content["data"]["transactionCreate"]["transaction"] + assert data["actions"] == available_actions + assert data["pspReference"] == psp_reference + assert data["authorizedAmount"]["amount"] == authorized_value + assert data["externalUrl"] == external_url + assert data["createdBy"]["id"] == to_global_id_or_none(app_api_client.app) + + assert available_actions == list(map(str.upper, transaction.available_actions)) + assert psp_reference == transaction.psp_reference + assert authorized_value == transaction.authorized_value + assert transaction.metadata == {} + assert transaction.private_metadata == {} + assert transaction.external_url == external_url + assert transaction.app_identifier == app_api_client.app.identifier + assert transaction.app == app_api_client.app + assert transaction.user is None + + @pytest.mark.parametrize( "amount_field_name, amount_db_field", [ diff --git a/saleor/graphql/payment/tests/mutations/test_transaction_update.py b/saleor/graphql/payment/tests/mutations/test_transaction_update.py index 73f3cdd1a0b..306ce482830 100644 --- a/saleor/graphql/payment/tests/mutations/test_transaction_update.py +++ b/saleor/graphql/payment/tests/mutations/test_transaction_update.py @@ -241,6 +241,31 @@ def test_transaction_update_metadata_by_app( assert transaction_item_created_by_app.metadata == {meta_key: meta_value} +def test_transaction_update_metadata_by_app_null_value( + transaction_item_created_by_app, permission_manage_payments, app_api_client +): + # given + transaction = transaction_item_created_by_app + + variables = { + "id": graphene.Node.to_global_id("TransactionItem", transaction.token), + "transaction": { + "metadata": None, + }, + } + + # when + response = app_api_client.post_graphql( + MUTATION_TRANSACTION_UPDATE, variables, permissions=[permission_manage_payments] + ) + + # then + transaction.refresh_from_db() + content = get_graphql_content(response) + data = content["data"]["transactionUpdate"]["transaction"] + assert len(data["metadata"]) == 0 + + def test_transaction_update_metadata_incorrect_key_by_app( transaction_item_created_by_app, permission_manage_payments, app_api_client ): @@ -300,6 +325,33 @@ def test_transaction_update_private_metadata_by_app( assert transaction_item_created_by_app.private_metadata == {meta_key: meta_value} +def test_transaction_update_private_metadata_by_app_null_value( + transaction_item_created_by_app, permission_manage_payments, app_api_client +): + # given + transaction = transaction_item_created_by_app + transaction.private_metadata = {"key": "value"} + transaction.save(update_fields=["private_metadata"]) + + variables = { + "id": graphene.Node.to_global_id("TransactionItem", transaction.token), + "transaction": { + "privateMetadata": None, + }, + } + + # when + response = app_api_client.post_graphql( + MUTATION_TRANSACTION_UPDATE, variables, permissions=[permission_manage_payments] + ) + + # then + transaction.refresh_from_db() + content = get_graphql_content(response) + data = content["data"]["transactionUpdate"]["transaction"] + assert len(data["privateMetadata"]) == 1 + + def test_transaction_update_private_metadata_incorrect_key_by_app( transaction_item_created_by_app, permission_manage_payments, app_api_client ):