diff --git a/lms/djangoapps/shoppingcart/processors/CyberSource2.py b/lms/djangoapps/shoppingcart/processors/CyberSource2.py index 5606279829..1d66138a96 100644 --- a/lms/djangoapps/shoppingcart/processors/CyberSource2.py +++ b/lms/djangoapps/shoppingcart/processors/CyberSource2.py @@ -79,6 +79,7 @@ def process_postpay_callback(params): 'error_html': '' } else: + _record_payment_info(params, result['order']) return { 'success': False, 'order': result['order'], @@ -86,6 +87,11 @@ def process_postpay_callback(params): } except CCProcessorException as error: log.exception('error processing CyberSource postpay callback') + # if we have the order and the id, log it + if hasattr(error, 'order'): + _record_payment_info(params, error.order) + else: + log.info(json.dumps(params)) return { 'success': False, 'order': None, # due to exception we may not have the order @@ -350,7 +356,7 @@ def _payment_accepted(order_id, auth_amount, currency, decision): 'order': order } else: - raise CCProcessorWrongAmountException( + ex = CCProcessorWrongAmountException( _( u"The amount charged by the processor {charged_amount} {charged_amount_currency} is different " u"than the total cost of the order {total_cost} {total_cost_currency}." @@ -361,6 +367,8 @@ def _payment_accepted(order_id, auth_amount, currency, decision): total_cost_currency=order.currency ) ) + ex.order = order + raise ex else: return { 'accepted': False, @@ -408,6 +416,20 @@ def _record_purchase(params, order): ) +def _record_payment_info(params, order): + """ + Record the purchase and run purchased_callbacks + + Args: + params (dict): The parameters we received from CyberSource. + + Returns: + None + """ + order.processor_reply_dump = json.dumps(params) + order.save() + + def _get_processor_decline_html(params): """ Return HTML indicating that the user's payment was declined. diff --git a/lms/djangoapps/shoppingcart/processors/exceptions.py b/lms/djangoapps/shoppingcart/processors/exceptions.py index 2bcf35e50f..47a51a8b60 100644 --- a/lms/djangoapps/shoppingcart/processors/exceptions.py +++ b/lms/djangoapps/shoppingcart/processors/exceptions.py @@ -16,5 +16,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 de3777222f..5a12fa5597 100644 --- a/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource2.py +++ b/lms/djangoapps/shoppingcart/processors/tests/test_CyberSource2.py @@ -49,6 +49,13 @@ class CyberSource2Test(TestCase): line_cost=self.COST ) + def assert_dump_recorded(self, order): + """ + Verify that this order does have a dump of information from the + payment processor. + """ + self.assertNotEqual(order.processor_reply_dump, '') + def test_render_purchase_form_html(self): # Verify that the HTML form renders with the payment URL specified # in the test settings. @@ -131,6 +138,7 @@ class CyberSource2Test(TestCase): # Expect that the order has been marked as purchased self.assertEqual(result['order'].status, 'purchased') + self.assert_dump_recorded(result['order']) def test_process_payment_rejected(self): # Simulate a callback from CyberSource indicating that the payment was rejected @@ -140,6 +148,7 @@ class CyberSource2Test(TestCase): # Expect that we get an error message self.assertFalse(result['success']) self.assertIn(u"did not accept your payment", result['error_html']) + self.assert_dump_recorded(result['order']) def test_process_payment_invalid_signature(self): # Simulate a callback from CyberSource indicating that the payment was rejected @@ -167,6 +176,9 @@ class CyberSource2Test(TestCase): # Expect an error self.assertFalse(result['success']) self.assertIn(u"different amount than the order total", result['error_html']) + # refresh data for current order + order = Order.objects.get(id=self.order.id) + self.assert_dump_recorded(order) def test_process_amount_paid_not_decimal(self): # Change the payment amount to a non-decimal @@ -202,6 +214,7 @@ class CyberSource2Test(TestCase): msg="Payment was not successful: {error}".format(error=result.get('error_html')) ) self.assertEqual(result['error_html'], '') + self.assert_dump_recorded(result['order']) # Expect that the order has placeholders for the missing credit card digits self.assertEqual(result['order'].bill_to_ccnum, '####') @@ -233,6 +246,7 @@ class CyberSource2Test(TestCase): # Verify that this executes without a unicode error result = process_postpay_callback(params) self.assertTrue(result['success']) + self.assert_dump_recorded(result['order']) @ddt.data('string', u'üñîçø∂é') def test_get_processor_exception_html(self, error_string):