Skip to content

Commit

Permalink
Feat(vipps)/check payment (#943)
Browse files Browse the repository at this point in the history
* format

* small fix

* Finished vipps confimation

* Fixed linting

* Fixed linting again

* change endpoint url, add permission check for endpoint

* Fix enum spelling

* fixed permission check, added test to make sure it works as it should

* fix linting

* added tests that verify only index members and members of the organizing group can update an order

* changed response key from 'is_true' to detail, and made the description more explanatory

* fikset skrivefeil

---------

Co-authored-by: 1Cezzo <[email protected]>
  • Loading branch information
LeMiTam and EmilJohns1 authored Jan 26, 2025
1 parent b695ee1 commit c07e432
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 8 deletions.
1 change: 0 additions & 1 deletion app/files/serializers/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ def create(self, validated_data):


class UpdateFileSerializer(BaseModelSerializer):

class Meta:
model = File
fields = (
Expand Down
24 changes: 21 additions & 3 deletions app/payment/models/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@

from django.db import models

from app.common.enums import Groups
from app.common.enums import AdminGroup, Groups
from app.common.permissions import (
BasePermissionModel,
check_has_access,
is_admin_user,
)
from app.content.models.event import Event
from app.content.models.user import User
from app.group.models.membership import Membership
from app.payment.enums import OrderStatus
from app.util.models import BaseModel


class Order(BaseModel, BasePermissionModel):
read_access = (Groups.TIHLDE,)
update_access = (AdminGroup.INDEX,)

order_id = models.UUIDField(
auto_created=True, default=uuid.uuid4, primary_key=True, serialize=False
Expand All @@ -40,6 +42,22 @@ def __str__(self):

@classmethod
def has_update_permission(cls, request):
if check_has_access(cls.update_access, request):
return True

order_id = request.parser_context.get("kwargs", {}).get("pk")

if order_id:
try:
order = Order.objects.get(order_id=order_id)

if order.event.organizer and order.event.organizer.slug:
return Membership.objects.filter(
user=request.user, group=order.event.organizer
).exists()
except Order.DoesNotExist:
return False

return False

@classmethod
Expand Down Expand Up @@ -75,8 +93,8 @@ def has_list_permission(cls, request):
def has_read_all_permission(cls, request):
return is_admin_user(request)

def has_object_update_permission(self, _request):
return False
def has_object_update_permission(self, request):
return self.has_update_permission(request)

def has_object_destroy_permission(self, _request):
return False
Expand Down
1 change: 1 addition & 0 deletions app/payment/serializers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@
VippsOrderSerialzer,
OrderListSerializer,
OrderUpdateSerializer,
CheckPaymentSerializer,
)
5 changes: 5 additions & 0 deletions app/payment/serializers/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ class Meta:
fields = ("status",)


class CheckPaymentSerializer(BaseModelSerializer):
class Meta:
fields = ("event_id", "user_id")


class OrderCreateSerializer(BaseModelSerializer):
class Meta:
model = Order
Expand Down
8 changes: 6 additions & 2 deletions app/payment/urls.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from django.urls import include, re_path
from django.urls import include, path, re_path
from rest_framework import routers

from app.payment.views.order import OrderViewSet
from app.payment.views.vipps import VippsViewSet
from app.payment.views.vipps_util import check_vipps_payment

router = routers.DefaultRouter()

Expand All @@ -11,4 +12,7 @@
r"v2/payments/(?P<order_id>[0-9a-f-]+)", VippsViewSet, basename="payment"
)

urlpatterns = [re_path(r"", include(router.urls))]
urlpatterns = [
re_path(r"", include(router.urls)),
path("check-payment/", check_vipps_payment),
]
2 changes: 2 additions & 0 deletions app/payment/views/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from app.payment.views.vipps_util import check_vipps_payment
from app.payment.serializers import CheckPaymentSerializer
49 changes: 49 additions & 0 deletions app/payment/views/vipps_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response

from app.payment.models import Order
from app.payment.serializers import CheckPaymentSerializer
from app.payment.util.payment_utils import get_payment_order_status


@api_view(["POST"])
def check_vipps_payment(self, request, *args, **kwargs):
has_changed = False

serializer = CheckPaymentSerializer(data=request.data)
serializer.is_valid(raise_exception=True)

event_id = serializer.validated_data["event_id"]
user_id = serializer.validated_data["user_id"]

orders = self.queryset.filter(user_id=user_id, event_id=event_id)

if not orders.exists():
return Response(
{"detail": "Ingen ordre funnet for bruker og arrangement."},
status=status.HTTP_404_NOT_FOUND,
)

if not Order.has_update_permission(self.request):
return Response(
{"detail": "Du har ikke tilgang til å oppdatere denne ordren."},
status=status.HTTP_403_FORBIDDEN,
)

for order in orders:
order_status = get_payment_order_status(order.order_id)
if order_status != order.status:
has_changed = True
order.status = order_status
order.save()

if has_changed(order):
return Response(
{"detail": "Ordrestatusen var feil og har blitt endret."},
status=status.HTTP_200_OK,
)
return Response(
{"detail": "Ordrestatusen er korrekt og har ikke blitt endret."},
status=status.HTTP_200_OK,
)
55 changes: 53 additions & 2 deletions app/tests/payment/test_order_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,16 +117,67 @@ def test_update_order_as_member(member, order):


@pytest.mark.django_db
@pytest.mark.parametrize("group_name", [*AdminGroup.admin()])
@pytest.mark.parametrize("group_name", [AdminGroup.INDEX])
def test_update_order_as_index_user(member, order, group_name):
"""An index user should be able to update an order."""
add_user_to_group_with_name(member, group_name)
client = get_api_client(user=member)
data = {"status": OrderStatus.SALE}
response = client.put(get_orders_url_detail(order.order_id), data=data)
assert response.status_code == status.HTTP_200_OK


@pytest.mark.django_db
@pytest.mark.parametrize(
"group_name", [AdminGroup.HS, AdminGroup.KOK, AdminGroup.SOSIALEN]
)
def test_update_order_as_admin_user(member, order, group_name):
"""An index and HS user should not be able to update an order."""
"""Members of admin groups other than index should not be able to update an order."""
add_user_to_group_with_name(member, group_name)
client = get_api_client(user=member)
data = {"status": OrderStatus.SALE}
response = client.put(get_orders_url_detail(order.order_id), data=data)
assert response.status_code == status.HTTP_403_FORBIDDEN


@pytest.mark.django_db
def test_update_order_as_organizer(member, event, order):
"""Test that members of the organizing group (e.g., SOSIALEN) can update an order."""
organizer_group = GroupFactory(name=AdminGroup.SOSIALEN)
add_user_to_group_with_name(member, organizer_group.name)

event.organizer = organizer_group
event.save()

order.event = event
order.save()

data = {"status": OrderStatus.SALE}
client = get_api_client(user=member)

response = client.put(get_orders_url_detail(order.order_id), data=data)
assert response.status_code == status.HTTP_200_OK


@pytest.mark.django_db
def test_update_order_as_non_organizer(member, event, order):
"""Test that members not in the organizing group do not have permission to update an order."""
organizer_group = GroupFactory(name=AdminGroup.SOSIALEN)
add_user_to_group_with_name(member, AdminGroup.NOK)

event.organizer = organizer_group
event.save()

order.event = event
order.save()

data = {"status": OrderStatus.SALE}
client = get_api_client(user=member)

response = client.put(get_orders_url_detail(order.order_id), data=data)
assert response.status_code == status.HTTP_403_FORBIDDEN


@pytest.mark.django_db
def test_list_all_orders_for_event_as_organizer(member, event):
"""
Expand Down

0 comments on commit c07e432

Please sign in to comment.