diff --git a/ecommerce/extensions/iap/processors/base_iap.py b/ecommerce/extensions/iap/processors/base_iap.py index 106a881e44e..2f28d6a79db 100644 --- a/ecommerce/extensions/iap/processors/base_iap.py +++ b/ecommerce/extensions/iap/processors/base_iap.py @@ -128,6 +128,11 @@ def handle_processor_response(self, response, basket=None): if not original_transaction_id: raise PaymentError(response) + if 'cancellation_reason' in validation_response['receipt']['in_app'][0]: + error = 'iOS payment is cancelled for [%s] in basket [%d]' + logger.error(error, original_transaction_id, basket.id) + raise UserCancelled(response) + # In case of Android transaction_id is required to identify payment elif not transaction_id: logger.error('Unable to find transaction id for basket [%d]', basket.id) @@ -178,8 +183,7 @@ def parse_ios_response(self, response, product_id): """ purchases = response['receipt'].get('in_app', []) for purchase in purchases: - if purchase['product_id'] == product_id and \ - response['receipt']['original_purchase_date_ms'] == purchase['original_purchase_date_ms']: + if purchase['product_id'] == product_id: response['receipt']['in_app'] = [purchase] break diff --git a/ecommerce/extensions/iap/tests/processors/test_ios_iap.py b/ecommerce/extensions/iap/tests/processors/test_ios_iap.py index b305b07282e..6ad118c021d 100644 --- a/ecommerce/extensions/iap/tests/processors/test_ios_iap.py +++ b/ecommerce/extensions/iap/tests/processors/test_ios_iap.py @@ -9,7 +9,7 @@ import mock from django.test import RequestFactory from django.urls import reverse -from oscar.apps.payment.exceptions import GatewayError, PaymentError +from oscar.apps.payment.exceptions import GatewayError, PaymentError, UserCancelled from oscar.core.loading import get_model from testfixtures import LogCapture @@ -185,6 +185,19 @@ def test_handle_processor_response_payment_error(self, mock_ios_validator): self.processor.handle_processor_response(modified_return_data, basket=self.basket) + @mock.patch.object(IOSValidator, 'validate') + def test_handle_cancelled_payment_error(self, mock_ios_validator): + """ + Verify that User cancelled exception is raised in presence of cancellation_reason parameter. + """ + modified_validation_response = self.mock_validation_response + modified_validation_response['receipt']['in_app'][2]['cancellation_reason'] = 0 + mock_ios_validator.return_value = modified_validation_response + with self.assertRaises(UserCancelled): + modified_return_data = self.RETURN_DATA + + self.processor.handle_processor_response(modified_return_data, basket=self.basket) + @mock.patch.object(IOSIAP, 'is_payment_redundant') @mock.patch.object(IOSValidator, 'validate') def test_handle_processor_response_redundant_error(self, mock_ios_validator, mock_payment_redundant):