Commit 2a449ade by Jim Abramson

Merge pull request #7938 from edx/jsa/xcom-287

fix ecommerce api calls in verify_student and add tests.
parents 9445534c cc1c7d50
......@@ -3,7 +3,6 @@ import json
from django.test import TestCase
from django.test.utils import override_settings
from ecommerce_api_client.client import EcommerceApiClient
import httpretty
import jwt
import mock
......@@ -12,15 +11,24 @@ from commerce import ecommerce_api_client
from student.tests.factories import UserFactory
TEST_API_URL = 'http://example.com/api'
TEST_API_SIGNING_KEY = 'edx'
TEST_BASKET_ID = 7
TEST_ORDER_NUMBER = '100004'
TEST_PAYMENT_DATA = {
'payment_processor_name': 'test-processor',
'payment_form_data': {},
'payment_page_url': 'http://example.com/pay',
}
class EcommerceApiClientTest(TestCase):
""" Tests to ensure the client is initialized properly. """
TEST_SIGNING_KEY = 'edx'
TEST_API_URL = 'http://example.com/api'
TEST_USER_EMAIL = 'test@example.com'
TEST_CLIENT_ID = 'test-client-id'
@override_settings(ECOMMERCE_API_SIGNING_KEY=TEST_SIGNING_KEY, ECOMMERCE_API_URL=TEST_API_URL)
@override_settings(ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY, ECOMMERCE_API_URL=TEST_API_URL)
@httpretty.activate
def test_tracking_context(self):
""" Ensure the tracking context is set up in the api client correctly
......@@ -32,7 +40,7 @@ class EcommerceApiClientTest(TestCase):
# fake an ecommerce api request.
httpretty.register_uri(
httpretty.POST,
'{}/baskets/1/'.format(self.TEST_API_URL),
'{}/baskets/1/'.format(TEST_API_URL),
status=200, body='{}',
adding_headers={'Content-Type': 'application/json'}
)
......@@ -51,82 +59,5 @@ class EcommerceApiClientTest(TestCase):
'lms_client_id': self.TEST_CLIENT_ID,
},
}
expected_header = 'JWT {}'.format(jwt.encode(expected_payload, self.TEST_SIGNING_KEY))
expected_header = 'JWT {}'.format(jwt.encode(expected_payload, TEST_API_SIGNING_KEY))
self.assertEqual(actual_header, expected_header)
class EcommerceApiTestMixin(object):
""" Mixin for tests utilizing the E-Commerce API. """
ECOMMERCE_API_URL = 'http://example.com/api'
ECOMMERCE_API_SIGNING_KEY = 'edx'
BASKET_ID = 7
ORDER_NUMBER = '100004'
PROCESSOR = 'test-processor'
PAYMENT_DATA = {
'payment_processor_name': PROCESSOR,
'payment_form_data': {},
'payment_page_url': 'http://example.com/pay',
}
ORDER_DATA = {'number': ORDER_NUMBER}
ECOMMERCE_API_SUCCESSFUL_BODY = {
'id': BASKET_ID,
'order': {'number': ORDER_NUMBER}, # never both None.
'payment_data': PAYMENT_DATA,
}
ECOMMERCE_API_SUCCESSFUL_BODY_JSON = json.dumps(ECOMMERCE_API_SUCCESSFUL_BODY) # pylint: disable=invalid-name
def _mock_ecommerce_api(self, status=200, body=None, is_payment_required=False):
"""
Mock calls to the E-Commerce API.
The calling test should be decorated with @httpretty.activate.
"""
self.assertTrue(httpretty.is_enabled(), 'Test is missing @httpretty.activate decorator.')
url = self.ECOMMERCE_API_URL + '/baskets/'
if body is None:
response_data = {'id': self.BASKET_ID, 'payment_data': None, 'order': None}
if is_payment_required:
response_data['payment_data'] = self.PAYMENT_DATA
else:
response_data['order'] = {'number': self.ORDER_NUMBER}
body = json.dumps(response_data)
httpretty.register_uri(httpretty.POST, url, status=status, body=body,
adding_headers={'Content-Type': 'application/json'})
class mock_create_basket(object): # pylint: disable=invalid-name
""" Mocks calls to E-Commerce API client basket creation method. """
patch = None
def __init__(self, **kwargs):
default_kwargs = {'return_value': EcommerceApiTestMixin.ECOMMERCE_API_SUCCESSFUL_BODY}
default_kwargs.update(kwargs)
_mock = mock.Mock()
_mock.post = mock.Mock(**default_kwargs)
EcommerceApiClient.baskets = _mock
self.patch = _mock
def __enter__(self):
return self.patch
def __exit__(self, exc_type, exc_val, exc_tb): # pylint: disable=unused-argument
pass
class mock_basket_order(object): # pylint: disable=invalid-name
""" Mocks calls to E-Commerce API client basket order method. """
patch = None
def __init__(self, **kwargs):
_mock = mock.Mock()
_mock.order.get = mock.Mock(**kwargs)
EcommerceApiClient.baskets = lambda client, basket_id: _mock
self.patch = _mock
def __enter__(self):
return self.patch
def __exit__(self, exc_type, exc_val, exc_tb): # pylint: disable=unused-argument
pass
""" Commerce app tests package. """
import json
import httpretty
from commerce.tests import TEST_API_URL
class mock_ecommerce_api_endpoint(object): # pylint: disable=invalid-name
"""
Base class for contextmanagers used to mock calls to api endpoints.
The contextmanager internally activates and deactivates httpretty as
required, therefore it is not advised to use this mock endpoint in
test cases where httpretty is being used directly.
"""
# override this in subclasses.
default_response = None
# override this in subclasses, using one of httpretty's method constants
method = None
def __init__(self, response=None, status=200, expect_called=True, exception=None):
"""
Keyword Arguments:
response: a JSON-serializable Python type representing the desired response body.
status: desired HTTP status for the response.
expect_called: a boolean indicating whether an API request was expected; set
to False if we should ensure that no request arrived.
exception: raise this exception instead of returning an HTTP response when called.
"""
self.response = response or self.default_response
self.status = status
self.expect_called = expect_called
self.exception = exception
def get_uri(self):
"""
Return the uri to register with httpretty for this contextmanager.
Subclasses must override this method.
"""
raise NotImplementedError
def _exception_body(self, request, uri, headers): # pylint: disable=unused-argument
"""Helper used to create callbacks in order to have httpretty raise Exceptions."""
raise self.exception # pylint: disable=raising-bad-type
def __enter__(self):
httpretty.reset()
httpretty.enable()
httpretty.register_uri(
self.method,
self.get_uri(),
status=self.status,
body=self._exception_body if self.exception is not None else json.dumps(self.response),
adding_headers={'Content-Type': 'application/json'},
)
def __exit__(self, exc_type, exc_val, exc_tb): # pylint: disable=unused-argument
assert self.expect_called == (httpretty.last_request().headers != {})
httpretty.disable()
class mock_create_basket(mock_ecommerce_api_endpoint): # pylint: disable=invalid-name
""" Mocks calls to E-Commerce API client basket creation method. """
default_response = {
'id': 7,
'order': {'number': '100004'}, # never both None.
'payment_data': {
'payment_processor_name': 'test-processor',
'payment_form_data': {},
'payment_page_url': 'http://example.com/pay',
},
}
method = httpretty.POST
def get_uri(self):
return TEST_API_URL + '/baskets/'
class mock_basket_order(mock_ecommerce_api_endpoint): # pylint: disable=invalid-name
""" Mocks calls to E-Commerce API client basket order method. """
default_response = {'number': 1}
method = httpretty.GET
def __init__(self, basket_id, **kwargs):
super(mock_basket_order, self).__init__(**kwargs)
self.basket_id = basket_id
def get_uri(self):
return TEST_API_URL + '/baskets/{}/order/'.format(self.basket_id)
......@@ -12,7 +12,8 @@ from xmodule.modulestore.tests.factories import CourseFactory
from ecommerce_api_client import exceptions
from commerce.constants import Messages
from commerce.tests import EcommerceApiTestMixin
from commerce.tests import TEST_BASKET_ID, TEST_ORDER_NUMBER, TEST_PAYMENT_DATA, TEST_API_URL, TEST_API_SIGNING_KEY
from commerce.tests.mocks import mock_basket_order, mock_create_basket
from course_modes.models import CourseMode
from enrollment.api import get_enrollment
from student.models import CourseEnrollment
......@@ -33,9 +34,8 @@ class UserMixin(object):
@ddt
@override_settings(ECOMMERCE_API_URL=EcommerceApiTestMixin.ECOMMERCE_API_URL,
ECOMMERCE_API_SIGNING_KEY=EcommerceApiTestMixin.ECOMMERCE_API_SIGNING_KEY)
class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixin, ModuleStoreTestCase):
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase):
"""
Tests for the commerce orders view.
"""
......@@ -60,7 +60,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
def assertResponsePaymentData(self, response):
""" Asserts correctness of a JSON body containing payment information. """
actual_response = json.loads(response.content)
self.assertEqual(actual_response, self.PAYMENT_DATA)
self.assertEqual(actual_response, TEST_PAYMENT_DATA)
def assertValidEcommerceInternalRequestErrorResponse(self, response):
""" Asserts the response is a valid response sent when the E-Commerce API is unavailable. """
......@@ -126,7 +126,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
"""
If the call to the E-Commerce API times out, the view should log an error and return an HTTP 503 status.
"""
with self.mock_create_basket(side_effect=exceptions.Timeout):
with mock_create_basket(exception=exceptions.Timeout):
response = self._post_to_view()
self.assertValidEcommerceInternalRequestErrorResponse(response)
......@@ -136,7 +136,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
"""
If the E-Commerce API raises an error, the view should return an HTTP 503 status.
"""
with self.mock_create_basket(side_effect=exceptions.SlumberBaseException):
with mock_create_basket(exception=exceptions.SlumberBaseException):
response = self._post_to_view()
self.assertValidEcommerceInternalRequestErrorResponse(response)
......@@ -150,7 +150,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
# Validate the response content
if is_completed:
msg = Messages.ORDER_COMPLETED.format(order_number=self.ORDER_NUMBER)
msg = Messages.ORDER_COMPLETED.format(order_number=TEST_ORDER_NUMBER)
self.assertResponseMessage(response, msg)
else:
self.assertResponsePaymentData(response)
......@@ -166,8 +166,8 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
self.user.is_active = user_is_active
self.user.save() # pylint: disable=no-member
return_value = {'id': self.BASKET_ID, 'payment_data': None, 'order': {'number': self.ORDER_NUMBER}}
with self.mock_create_basket(return_value=return_value):
return_value = {'id': TEST_BASKET_ID, 'payment_data': None, 'order': {'number': TEST_ORDER_NUMBER}}
with mock_create_basket(response=return_value):
self._test_successful_ecommerce_api_call()
@data(True, False)
......@@ -180,8 +180,8 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
self.user.is_active = user_is_active
self.user.save() # pylint: disable=no-member
return_value = {'id': self.BASKET_ID, 'payment_data': self.PAYMENT_DATA, 'order': None}
with self.mock_create_basket(return_value=return_value):
return_value = {'id': TEST_BASKET_ID, 'payment_data': TEST_PAYMENT_DATA, 'order': None}
with mock_create_basket(response=return_value):
self._test_successful_ecommerce_api_call(False)
def _test_course_without_sku(self):
......@@ -189,7 +189,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
Validates the view bypasses the E-Commerce API when the course has no CourseModes with SKUs.
"""
# Place an order
with self.mock_create_basket() as api_mock:
with mock_create_basket(expect_called=False):
response = self._post_to_view()
# Validate the response content
......@@ -198,9 +198,6 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
username=self.user.username)
self.assertResponseMessage(response, msg)
# No calls made to the E-Commerce API
self.assertFalse(api_mock.called)
def test_course_without_sku(self):
"""
If the course does NOT have a SKU, the user should be enrolled in the course (under the honor mode) and
......@@ -218,7 +215,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
"""
If the E-Commerce Service is not configured, the view should enroll the user.
"""
with self.mock_create_basket() as api_mock:
with mock_create_basket(expect_called=False):
response = self._post_to_view()
# Validate the response
......@@ -228,7 +225,6 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
# Ensure that the user is not enrolled and that no calls were made to the E-Commerce API
self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id))
self.assertFalse(api_mock.called)
def assertProfessionalModeBypassed(self):
""" Verifies that the view returns HTTP 406 when a course with no honor mode is encountered. """
......@@ -238,7 +234,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
CourseModeFactory.create(course_id=self.course.id, mode_slug=mode, mode_display_name=mode,
sku=uuid4().hex.decode('ascii'))
with self.mock_create_basket() as api_mock:
with mock_create_basket(expect_called=False):
response = self._post_to_view()
# The view should return an error status code
......@@ -246,9 +242,6 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
msg = Messages.NO_HONOR_MODE.format(course_id=self.course.id)
self.assertResponseMessage(response, msg)
# No calls should be made to the E-Commerce API.
self.assertFalse(api_mock.called)
def test_course_with_professional_mode_only(self):
""" Verifies that the view behaves appropriately when the course only has a professional mode. """
self.assertProfessionalModeBypassed()
......@@ -293,7 +286,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, EcommerceApiTestMixin, UserMixi
self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id))
self.assertIsNotNone(get_enrollment(self.user.username, unicode(self.course.id)))
with self.mock_create_basket():
with mock_create_basket():
self._test_successful_ecommerce_api_call(False)
......@@ -310,9 +303,8 @@ class OrdersViewTests(BasketsViewTests):
self.url = reverse('commerce:orders')
@override_settings(ECOMMERCE_API_URL=EcommerceApiTestMixin.ECOMMERCE_API_URL,
ECOMMERCE_API_SIGNING_KEY=EcommerceApiTestMixin.ECOMMERCE_API_SIGNING_KEY)
class BasketOrderViewTests(UserMixin, EcommerceApiTestMixin, TestCase):
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
class BasketOrderViewTests(UserMixin, TestCase):
""" Tests for the basket order view. """
view_name = 'commerce:basket_order'
MOCK_ORDER = {'number': 1}
......@@ -325,7 +317,7 @@ class BasketOrderViewTests(UserMixin, EcommerceApiTestMixin, TestCase):
def test_order_found(self):
""" If the order is located, the view should pass the data from the API. """
with self.mock_basket_order(return_value=self.MOCK_ORDER):
with mock_basket_order(basket_id=1, response=self.MOCK_ORDER):
response = self.client.get(self.path)
self.assertEqual(response.status_code, 200)
......@@ -334,7 +326,7 @@ class BasketOrderViewTests(UserMixin, EcommerceApiTestMixin, TestCase):
def test_order_not_found(self):
""" If the order is not found, the view should return a 404. """
with self.mock_basket_order(side_effect=exceptions.HttpNotFoundError):
with mock_basket_order(basket_id=1, exception=exceptions.HttpNotFoundError):
response = self.client.get(self.path)
self.assertEqual(response.status_code, 404)
......
......@@ -18,6 +18,7 @@ from django.conf import settings
from django.core.urlresolvers import reverse
from django.core.exceptions import ObjectDoesNotExist
from django.core import mail
import httpretty
from bs4 import BeautifulSoup
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
......@@ -27,7 +28,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from opaque_keys.edx.locator import CourseLocator
from openedx.core.djangoapps.user_api.accounts.api import get_account_settings
from commerce.tests import EcommerceApiTestMixin
from commerce.tests import TEST_PAYMENT_DATA, TEST_API_URL, TEST_API_SIGNING_KEY
from student.tests.factories import UserFactory, CourseEnrollmentFactory
from student.models import CourseEnrollment
from course_modes.tests.factories import CourseModeFactory
......@@ -36,8 +37,11 @@ from shoppingcart.models import Order, CertificateItem
from embargo.test_utils import restrict_course
from util.testing import UrlResetMixin
from verify_student.views import (
render_to_response, PayAndVerifyView, EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW,
EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY
checkout_with_ecommerce_service,
EVENT_NAME_USER_ENTERED_INCOURSE_REVERIFY_VIEW,
EVENT_NAME_USER_SUBMITTED_INCOURSE_REVERIFY,
PayAndVerifyView,
render_to_response,
)
from verify_student.models import (
SoftwareSecurePhotoVerification, VerificationCheckpoint,
......@@ -650,13 +654,18 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase):
course.start = kwargs.get('course_start')
modulestore().update_item(course, ModuleStoreEnum.UserID.test)
mode_kwargs = {}
if kwargs.get('sku'):
mode_kwargs['sku'] = kwargs['sku']
for course_mode in course_modes:
min_price = (0 if course_mode in ["honor", "audit"] else self.MIN_PRICE)
CourseModeFactory(
course_id=course.id,
mode_slug=course_mode,
mode_display_name=course_mode,
min_price=min_price
min_price=min_price,
**mode_kwargs
)
return course
......@@ -819,6 +828,35 @@ class TestPayAndVerifyView(UrlResetMixin, ModuleStoreTestCase):
self.assertEqual(response_dict['course_name'], mode_display_name)
@httpretty.activate
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
def test_processors_api(self):
"""
Check that when working with a product being processed by the
ecommerce api, we correctly call to that api for the list of
available payment processors.
"""
# setting a nonempty sku on the course will a trigger calls to
# the ecommerce api to get payment processors.
course = self._create_course("verified", sku='nonempty-sku')
self._enroll(course.id, "honor")
# mock out the payment processors endpoint
httpretty.register_uri(
httpretty.GET,
"{}/payment/processors/".format(TEST_API_URL),
body=json.dumps(['foo', 'bar']),
content_type="application/json",
)
# make the server request
response = self._get_page('verify_student_start_flow', course.id)
self.assertEqual(response.status_code, 200)
# ensure the mock api call was made. NOTE: the following line
# approximates the check - if the headers were empty it means
# there was no last request.
self.assertNotEqual(httpretty.last_request().headers, {})
class CheckoutTestMixin(object):
"""
......@@ -927,7 +965,7 @@ class CheckoutTestMixin(object):
# ensure the response to a request from a stale js client is modified so as
# not to break behavior in the browser.
# (XCOM-214) remove after release.
expected_payment_data = EcommerceApiTestMixin.PAYMENT_DATA.copy()
expected_payment_data = TEST_PAYMENT_DATA.copy()
expected_payment_data['payment_form_data'].update({'foo': 'bar'})
patched_create_order.return_value = expected_payment_data
# there is no 'processor' parameter in the post payload, so the response should only contain payment form data.
......@@ -945,7 +983,7 @@ class CheckoutTestMixin(object):
self.assertEqual(data, {'foo': 'bar'})
@patch('verify_student.views.checkout_with_shoppingcart', return_value=EcommerceApiTestMixin.PAYMENT_DATA)
@patch('verify_student.views.checkout_with_shoppingcart', return_value=TEST_PAYMENT_DATA)
class TestCreateOrderShoppingCart(CheckoutTestMixin, ModuleStoreTestCase):
""" Test view behavior when the shoppingcart is used. """
......@@ -958,12 +996,9 @@ class TestCreateOrderShoppingCart(CheckoutTestMixin, ModuleStoreTestCase):
return dict(zip(('request', 'user', 'course_key', 'course_mode', 'amount'), patched_create_order.call_args[0]))
@override_settings(
ECOMMERCE_API_URL=EcommerceApiTestMixin.ECOMMERCE_API_URL,
ECOMMERCE_API_SIGNING_KEY=EcommerceApiTestMixin.ECOMMERCE_API_SIGNING_KEY
)
@patch('verify_student.views.checkout_with_ecommerce_service', return_value=EcommerceApiTestMixin.PAYMENT_DATA)
class TestCreateOrderEcommerceService(CheckoutTestMixin, EcommerceApiTestMixin, ModuleStoreTestCase):
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
@patch('verify_student.views.checkout_with_ecommerce_service', return_value=TEST_PAYMENT_DATA)
class TestCreateOrderEcommerceService(CheckoutTestMixin, ModuleStoreTestCase):
""" Test view behavior when the ecommerce service is used. """
def make_sku(self):
......@@ -975,6 +1010,40 @@ class TestCreateOrderEcommerceService(CheckoutTestMixin, EcommerceApiTestMixin,
return dict(zip(('user', 'course_key', 'course_mode', 'processor'), patched_create_order.call_args[0]))
class TestCheckoutWithEcommerceService(ModuleStoreTestCase):
"""
Ensures correct behavior in the function `checkout_with_ecommerce_service`.
"""
@httpretty.activate
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
def test_create_basket(self):
"""
Check that when working with a product being processed by the
ecommerce api, we correctly call to that api to create a basket.
"""
user = UserFactory.create(username="test-username")
course_mode = CourseModeFactory(sku="test-sku")
expected_payment_data = {'foo': 'bar'}
# mock out the payment processors endpoint
httpretty.register_uri(
httpretty.POST,
"{}/baskets/".format(TEST_API_URL),
body=json.dumps({'payment_data': expected_payment_data}),
content_type="application/json",
)
# call the function
actual_payment_data = checkout_with_ecommerce_service(user, 'dummy-course-key', course_mode, 'test-processor')
# check the api call
self.assertEqual(json.loads(httpretty.last_request().body), {
'products': [{'sku': 'test-sku'}],
'checkout': True,
'payment_processor_name': 'test-processor',
})
# check the response
self.assertEqual(actual_payment_data, expected_payment_data)
class TestCreateOrderView(ModuleStoreTestCase):
"""
Tests for the create_order view of verified course enrollment process.
......
......@@ -384,7 +384,7 @@ class PayAndVerifyView(View):
# get available payment processors
if unexpired_paid_course_mode.sku:
# transaction will be conducted via ecommerce service
processors = ecommerce_api_client(request.user).get_processors()
processors = ecommerce_api_client(request.user).payment.processors.get()
else:
# transaction will be conducted using legacy shopping cart
processors = [settings.CC_PROCESSOR_NAME]
......@@ -657,9 +657,13 @@ def checkout_with_ecommerce_service(user, course_key, course_mode, processor):
try:
api = ecommerce_api_client(user)
# Make an API call to create the order and retrieve the results
response_data = api.create_basket(course_mode.sku, processor)
result = api.baskets.post({
'products': [{'sku': course_mode.sku}],
'checkout': True,
'payment_processor_name': processor
})
# Pass the payment parameters directly from the API response.
return response_data.get('payment_data')
return result.get('payment_data')
except SlumberBaseException:
params = {'username': user.username, 'mode': course_mode.slug, 'course_id': unicode(course_key)}
log.exception('Failed to create order for %(username)s %(mode)s mode of %(course_id)s', params)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment