From 68cdf3a59de9f88108738d43663e026707242b1f Mon Sep 17 00:00:00 2001 From: Chris Dodge Date: Thu, 11 Sep 2014 15:29:57 -0400 Subject: [PATCH] pass along an extra parameter to CyberSource to sepcify a callback URL if the user clicks cancel typo --- .../shoppingcart/processors/CyberSource2.py | 17 +++++++++++++++++ .../shoppingcart/processors/exceptions.py | 3 +++ .../processors/tests/test_CyberSource2.py | 13 ++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/shoppingcart/processors/CyberSource2.py b/lms/djangoapps/shoppingcart/processors/CyberSource2.py index dffe5c1ed2..fb9a5cb469 100644 --- a/lms/djangoapps/shoppingcart/processors/CyberSource2.py +++ b/lms/djangoapps/shoppingcart/processors/CyberSource2.py @@ -126,6 +126,12 @@ def verify_signatures(params): (missing keys, wrong types) """ + + # First see if the user cancelled the transaction + # if so, then not all parameters will be passed back so we can't yet verify signatures + if params.get('decision') == u'CANCEL': + raise CCProcessorUserCancelled() + # Validate the signature to ensure that the message is from CyberSource # and has not been tampered with. signed_fields = params.get('signed_field_names', '').split(',') @@ -272,6 +278,7 @@ def get_purchase_params(cart, callback_url=None): if callback_url is not None: params['override_custom_receipt_page'] = callback_url + params['override_custom_cancel_page'] = callback_url return params @@ -461,6 +468,16 @@ def _get_processor_exception_html(exception): email=payment_support_email ) ) + elif isinstance(exception, CCProcessorUserCancelled): + return _format_error_html( + _( + u"Sorry! Our payment processor sent us back a message saying that you have cancelled this transaction. " + u"The items in your shopping cart will exist for future purchase. " + u"If you feel that this is in error, please contact us with payment-specific questions at {email}." + ).format( + email=payment_support_email + ) + ) else: return _format_error_html( _( diff --git a/lms/djangoapps/shoppingcart/processors/exceptions.py b/lms/djangoapps/shoppingcart/processors/exceptions.py index 202f143cce..2bcf35e50f 100644 --- a/lms/djangoapps/shoppingcart/processors/exceptions.py +++ b/lms/djangoapps/shoppingcart/processors/exceptions.py @@ -15,3 +15,6 @@ class CCProcessorDataException(CCProcessorException): class CCProcessorWrongAmountException(CCProcessorException): pass + +class CCProcessorUserCancelled(CCProcessorException): + pass diff --git a/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource2.py b/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource2.py index a1c9b619c5..2636b44dc6 100644 --- a/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource2.py +++ b/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource2.py @@ -94,7 +94,8 @@ class CyberSource2Test(TestCase): 'unsigned_field_names', 'transaction_uuid', 'payment_method', - 'override_custom_receipt_page' + 'override_custom_receipt_page', + 'override_custom_cancel_page', ]) ) self.assertEqual(params['unsigned_field_names'], '') @@ -169,6 +170,16 @@ class CyberSource2Test(TestCase): self.assertFalse(result['success']) self.assertIn(u"badly-typed value", result['error_html']) + def test_process_user_cancelled(self): + # Change the payment amount to a non-decimal + params = self._signed_callback_params(self.order.id, self.COST, "abcd") + params['decision'] = u'CANCEL' + result = process_postpay_callback(params) + + # Expect an error + self.assertFalse(result['success']) + self.assertIn(u"you have cancelled this transaction", result['error_html']) + @patch.object(OrderItem, 'purchased_callback') def test_process_no_credit_card_digits(self, callback): # Use a credit card number with no digits provided