From 8c6b152dfe8a8c9f9946f6f6c674f0e08be32b82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Thu, 26 May 2016 13:04:47 +0200 Subject: [PATCH] lingo: add API endpoint to cancel a basket item (#9794) --- combo/apps/lingo/models.py | 5 +++-- combo/apps/lingo/urls.py | 5 ++++- combo/apps/lingo/views.py | 42 ++++++++++++++++++++++++++++++++++++++++++ tests/test_lingo_payment.py | 31 ++++++++++++++++++++++++++++--- 4 files changed, 77 insertions(+), 6 deletions(-) diff --git a/combo/apps/lingo/models.py b/combo/apps/lingo/models.py index cb3987d..3e0f376 100644 --- a/combo/apps/lingo/models.py +++ b/combo/apps/lingo/models.py @@ -216,8 +216,9 @@ class BasketItem(models.Model): self.notification_date = timezone.now() self.save() - def notify_cancellation(self): - self.notify('cancelled') + def notify_cancellation(self, skip_notification=False): + if not skip_notification: + self.notify('cancelled') self.cancellation_date = timezone.now() self.save() diff --git a/combo/apps/lingo/urls.py b/combo/apps/lingo/urls.py index ff5674b..f8677dc 100644 --- a/combo/apps/lingo/urls.py +++ b/combo/apps/lingo/urls.py @@ -19,7 +19,8 @@ from django.conf.urls import patterns, url, include from combo.urls_utils import decorated_includes, manager_required from .views import (RegiesApiView, AddBasketItemApiView, PayView, CallbackView, - ReturnView, ItemDownloadView, ItemView, CancelItemView) + ReturnView, ItemDownloadView, ItemView, CancelItemView, + RemoveBasketItemApiView) from .manager_views import (RegieListView, RegieCreateView, RegieUpdateView, RegieDeleteView, TransactionListView, ManagerHomeView) @@ -38,6 +39,8 @@ urlpatterns = patterns('', url('^api/lingo/regies$', RegiesApiView.as_view(), name='api-regies'), url('^api/lingo/add-basket-item$', AddBasketItemApiView.as_view(), name='api-add-basket-item'), + url('^api/lingo/remove-basket-item$', RemoveBasketItemApiView.as_view(), + name='api-remove-basket-item'), url(r'^lingo/pay$', PayView.as_view(), name='lingo-pay'), url(r'^lingo/cancel/(?P\w+)/$', CancelItemView.as_view(), name='lingo-cancel-item'), url(r'^lingo/callback/(?P\w+)/$', CallbackView.as_view(), name='lingo-callback'), diff --git a/combo/apps/lingo/views.py b/combo/apps/lingo/views.py index c5fd1a6..00d36d5 100644 --- a/combo/apps/lingo/views.py +++ b/combo/apps/lingo/views.py @@ -136,9 +136,51 @@ class AddBasketItemApiView(View): item.save() response = HttpResponse(content_type='application/json') + response.write(json.dumps({'result': 'success', 'id': str(item.id)})) + return response + + +class RemoveBasketItemApiView(View): + http_method_names = ['post', 'options'] + + @csrf_exempt + def dispatch(self, *args, **kwargs): + return super(RemoveBasketItemApiView, self).dispatch(*args, **kwargs) + + def post(self, request, *args, **kwargs): + key = getattr(settings, 'LINGO_API_SIGN_KEY', '12345') + if not check_query(request.META['QUERY_STRING'], key): + return HttpResponseForbidden() + + request_body = json.loads(self.request.body) + + if not 'basket_item_id' in request_body: + raise Exception('missing basket_item_id parameter') + + try: + if request.GET.get('NameId'): + if UserSAMLIdentifier is None: + raise Exception('missing mellon?') + try: + user = UserSAMLIdentifier.objects.get(name_id=request.GET.get('NameId')).user + except UserSAMLIdentifier.DoesNotExist: + raise Exception('unknown name id') + elif request.GET.get('email'): + user = User.objects.get(email=request.GET.get('email')) + else: + raise Exception('no user specified') + except User.DoesNotExist: + raise Exception('unknown user') + + item = BasketItem.objects.get(id=request_body.get('basket_item_id'), + user=user, cancellation_date__isnull=True) + item.notify_cancellation(skip_notification=bool(request_body.get('skip_notification'))) + + response = HttpResponse(content_type='application/json') response.write(json.dumps({'result': 'success'})) return response + class PayView(View): def post(self, request, *args, **kwargs): regie_id = request.POST.get('regie') diff --git a/tests/test_lingo_payment.py b/tests/test_lingo_payment.py index b8a4f69..52b612a 100644 --- a/tests/test_lingo_payment.py +++ b/tests/test_lingo_payment.py @@ -102,14 +102,14 @@ def test_add_amount_to_basket(regie, user): url = sign_url(url, settings.LINGO_API_SIGN_KEY) resp = client.post(url, json.dumps(data), content_type='application/json') assert resp.status_code == 200 - assert json.loads(resp.content) == {'result': 'success'} + assert json.loads(resp.content)['result'] == 'success' assert BasketItem.objects.filter(amount=amount).exists() data['extra'] = {'amount': '22.22'} url = sign_url(url, settings.LINGO_API_SIGN_KEY) resp = client.post(url, json.dumps(data), content_type='application/json') assert resp.status_code == 200 - assert json.loads(resp.content) == {'result': 'success'} + assert json.loads(resp.content)['result'] == 'success' assert BasketItem.objects.filter(amount=Decimal('64.22')).exists() data['amount'] = [amount] @@ -117,9 +117,34 @@ def test_add_amount_to_basket(regie, user): resp = client.post('%s&amount=5' % url, json.dumps(data), content_type='application/json') assert resp.status_code == 200 - assert json.loads(resp.content) == {'result': 'success'} + assert json.loads(resp.content)['result'] == 'success' assert BasketItem.objects.filter(amount=Decimal('81.22')).exists() +def test_cancel_basket_item(regie, user): + user_email = 'foo@example.com' + User.objects.get_or_create(email=user_email) + url = '%s?email=%s' % (reverse('api-add-basket-item'), user_email) + url = sign_url(url, settings.LINGO_API_SIGN_KEY) + data = {'amount': 42, 'display_name': 'test amount', 'url': 'http://example.com'} + resp = client.post(url, json.dumps(data), content_type='application/json') + assert resp.status_code == 200 + assert json.loads(resp.content)['result'] == 'success' + assert BasketItem.objects.filter(amount=42, cancellation_date__isnull=True).exists() + basket_item_id = json.loads(resp.content)['id'] + + data = {'amount': 21, 'display_name': 'test amount', 'url': 'http://example.com'} + resp = client.post(url, json.dumps(data), content_type='application/json') + assert resp.status_code == 200 + assert json.loads(resp.content)['result'] == 'success' + assert BasketItem.objects.filter(amount=42, cancellation_date__isnull=True).exists() + assert BasketItem.objects.filter(amount=21, cancellation_date__isnull=True).exists() + + url = '%s?email=%s' % (reverse('api-remove-basket-item'), user_email) + url = sign_url(url, settings.LINGO_API_SIGN_KEY) + data = {'basket_item_id': basket_item_id, 'skip_notification': True} + resp = client.post(url, json.dumps(data), content_type='application/json') + assert not BasketItem.objects.filter(amount=42, cancellation_date__isnull=True).exists() + assert BasketItem.objects.filter(amount=21, cancellation_date__isnull=True).exists() def test_payment_callback(regie, user): item = BasketItem.objects.create(user=user, regie=regie, -- 2.8.1