Updated receipt page to use order endpoint
The receipt page now retrieves data for orders instead of baskets. Going forward baskets will be deleted after an order has been placed, so there should be no permanent references to baskets. Orders will continue to be persisted permanently. ECOM-2653
This commit is contained in:
committed by
Clinton Blackburn
parent
352c5d13ac
commit
9bb3f703d7
@@ -7,7 +7,7 @@ from commerce.api.v0 import views
|
||||
BASKET_URLS = patterns(
|
||||
'',
|
||||
url(r'^$', views.BasketsView.as_view(), name='create'),
|
||||
url(r'^{}/order/$'.format(r'(?P<basket_id>[\w]+)'), views.BasketOrderView.as_view(), name='retrieve_order'),
|
||||
url(r'^(?P<basket_id>[\w]+)/order/$', views.BasketOrderView.as_view(), name='retrieve_order'),
|
||||
)
|
||||
|
||||
urlpatterns = patterns(
|
||||
|
||||
@@ -7,13 +7,19 @@ import ddt
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from edx_rest_api_client import exceptions
|
||||
from flaky import flaky
|
||||
from nose.plugins.attrib import attr
|
||||
import pytz
|
||||
from rest_framework.utils.encoders import JSONEncoder
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
from commerce.tests import TEST_API_URL, TEST_API_SIGNING_KEY
|
||||
from commerce.tests.mocks import mock_order_endpoint
|
||||
from commerce.tests.test_views import UserMixin
|
||||
from course_modes.models import CourseMode
|
||||
from student.tests.factories import UserFactory
|
||||
from verify_student.models import VerificationDeadline
|
||||
@@ -307,3 +313,38 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
|
||||
]
|
||||
}
|
||||
self.assertDictEqual(expected_dict, json.loads(response.content))
|
||||
|
||||
|
||||
@attr('shard_1')
|
||||
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
|
||||
class OrderViewTests(UserMixin, TestCase):
|
||||
""" Tests for the basket order view. """
|
||||
view_name = 'commerce_api:v1:orders:detail'
|
||||
ORDER_NUMBER = 'EDX-100001'
|
||||
MOCK_ORDER = {'number': ORDER_NUMBER}
|
||||
path = reverse(view_name, kwargs={'number': ORDER_NUMBER})
|
||||
|
||||
def setUp(self):
|
||||
super(OrderViewTests, self).setUp()
|
||||
self._login()
|
||||
|
||||
def test_order_found(self):
|
||||
""" If the order is located, the view should pass the data from the API. """
|
||||
with mock_order_endpoint(order_number=self.ORDER_NUMBER, response=self.MOCK_ORDER):
|
||||
response = self.client.get(self.path)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
actual = json.loads(response.content)
|
||||
self.assertEqual(actual, self.MOCK_ORDER)
|
||||
|
||||
def test_order_not_found(self):
|
||||
""" If the order is not found, the view should return a 404. """
|
||||
with mock_order_endpoint(order_number=self.ORDER_NUMBER, exception=exceptions.HttpNotFoundError):
|
||||
response = self.client.get(self.path)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_login_required(self):
|
||||
""" The view should return 403 if the user is not logged in. """
|
||||
self.client.logout()
|
||||
response = self.client.get(self.path)
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
@@ -4,15 +4,19 @@ from django.conf.urls import patterns, url, include
|
||||
|
||||
from commerce.api.v1 import views
|
||||
|
||||
|
||||
COURSE_URLS = patterns(
|
||||
'',
|
||||
url(r'^$', views.CourseListView.as_view(), name='list'),
|
||||
url(r'^{}/$'.format(settings.COURSE_ID_PATTERN), views.CourseRetrieveUpdateView.as_view(), name='retrieve_update'),
|
||||
)
|
||||
|
||||
ORDER_URLS = patterns(
|
||||
'',
|
||||
url(r'^(?P<number>[-\w]+)/$', views.OrderView.as_view(), name='detail'),
|
||||
)
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^courses/', include(COURSE_URLS, namespace='courses')),
|
||||
|
||||
url(r'^orders/', include(ORDER_URLS, namespace='orders')),
|
||||
)
|
||||
|
||||
@@ -2,16 +2,20 @@
|
||||
import logging
|
||||
|
||||
from django.http import Http404
|
||||
from edx_rest_api_client import exceptions
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework_oauth.authentication import OAuth2Authentication
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.generics import RetrieveUpdateAPIView, ListAPIView
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework_oauth.authentication import OAuth2Authentication
|
||||
|
||||
from commerce import ecommerce_api_client
|
||||
from commerce.api.v1.models import Course
|
||||
from commerce.api.v1.permissions import ApiKeyOrModelPermission
|
||||
from commerce.api.v1.serializers import CourseSerializer
|
||||
from course_modes.models import CourseMode
|
||||
from openedx.core.lib.api.mixins import PutAsCreateMixin
|
||||
from util.json_request import JsonResponse
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -54,3 +58,18 @@ class CourseRetrieveUpdateView(PutAsCreateMixin, RetrieveUpdateAPIView):
|
||||
# There is nothing to pre-save. The default behavior changes the Course.id attribute from
|
||||
# a CourseKey to a string, which is not desired.
|
||||
pass
|
||||
|
||||
|
||||
class OrderView(APIView):
|
||||
""" Retrieve order details. """
|
||||
|
||||
authentication_classes = (SessionAuthentication,)
|
||||
permission_classes = (IsAuthenticated,)
|
||||
|
||||
def get(self, request, number): # pylint:disable=unused-argument
|
||||
""" HTTP handler. """
|
||||
try:
|
||||
order = ecommerce_api_client(request.user).orders(number).get()
|
||||
return JsonResponse(order)
|
||||
except exceptions.HttpNotFoundError:
|
||||
return JsonResponse(status=404)
|
||||
|
||||
@@ -103,3 +103,17 @@ class mock_create_refund(mock_ecommerce_api_endpoint): # pylint: disable=invali
|
||||
|
||||
def get_uri(self):
|
||||
return TEST_API_URL + '/refunds/'
|
||||
|
||||
|
||||
class mock_order_endpoint(mock_ecommerce_api_endpoint): # pylint: disable=invalid-name
|
||||
""" Mocks calls to E-Commerce API client basket order method. """
|
||||
|
||||
default_response = {'number': 'EDX-100001'}
|
||||
method = httpretty.GET
|
||||
|
||||
def __init__(self, order_number, **kwargs):
|
||||
super(mock_order_endpoint, self).__init__(**kwargs)
|
||||
self.order_number = order_number
|
||||
|
||||
def get_uri(self):
|
||||
return TEST_API_URL + '/orders/{}/'.format(self.order_number)
|
||||
|
||||
@@ -10,9 +10,13 @@ var edx = edx || {};
|
||||
|
||||
edx.commerce.ReceiptView = Backbone.View.extend({
|
||||
useEcommerceApi: true,
|
||||
ecommerceBasketId: null,
|
||||
ecommerceOrderNumber: null,
|
||||
|
||||
initialize: function () {
|
||||
this.useEcommerceApi = !!($.url('?basket_id'));
|
||||
this.ecommerceBasketId = $.url('?basket_id');
|
||||
this.ecommerceOrderNumber = $.url('?orderNum');
|
||||
this.useEcommerceApi = this.ecommerceBasketId || this.ecommerceOrderNumber;
|
||||
_.bindAll(this, 'renderReceipt', 'renderError', 'getProviderData', 'renderProvider', 'getCourseData');
|
||||
|
||||
/* Mix non-conflicting functions from underscore.string (all but include, contains, and reverse) into
|
||||
@@ -75,7 +79,7 @@ var edx = edx || {};
|
||||
|
||||
render: function () {
|
||||
var self = this,
|
||||
orderId = $.url('?basket_id') || $.url('?payment-order-num');
|
||||
orderId = this.ecommerceOrderNumber || this.ecommerceBasketId || $.url('?payment-order-num');
|
||||
|
||||
if (orderId && this.$el.data('is-payment-complete') === 'True') {
|
||||
// Get the order details
|
||||
@@ -106,14 +110,21 @@ var edx = edx || {};
|
||||
|
||||
/**
|
||||
* Retrieve receipt data from Oscar (via LMS).
|
||||
* @param {int} basketId The basket that was purchased.
|
||||
* @param {string} orderId Identifier of the order that was purchased.
|
||||
* @return {object} JQuery Promise.
|
||||
*/
|
||||
getReceiptData: function (basketId) {
|
||||
var urlFormat = this.useEcommerceApi ? '/api/commerce/v0/baskets/%s/order/' : '/shoppingcart/receipt/%s/';
|
||||
getReceiptData: function (orderId) {
|
||||
var urlFormat = '/shoppingcart/receipt/%s/';
|
||||
|
||||
if (this.ecommerceOrderNumber) {
|
||||
urlFormat = '/api/commerce/v1/orders/%s/';
|
||||
} else if (this.ecommerceBasketId){
|
||||
urlFormat = '/api/commerce/v0/baskets/%s/order/';
|
||||
}
|
||||
|
||||
|
||||
return $.ajax({
|
||||
url: _.sprintf(urlFormat, basketId),
|
||||
url: _.sprintf(urlFormat, orderId),
|
||||
type: 'GET',
|
||||
dataType: 'json'
|
||||
}).retry({times: 5, timeout: 2000, statusCodes: [404]});
|
||||
|
||||
Reference in New Issue
Block a user