Skip to content

Commit

Permalink
Merge branch 'develop' into setup-notifications-bot
Browse files Browse the repository at this point in the history
  • Loading branch information
Kateryna-Bordonos17 authored Dec 18, 2023
2 parents bf8da91 + c23e79c commit f5da51c
Show file tree
Hide file tree
Showing 16 changed files with 172 additions and 66 deletions.
18 changes: 18 additions & 0 deletions book_service/views.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from drf_spectacular.utils import extend_schema, OpenApiParameter
from rest_framework import viewsets

from book_service.models import Book
Expand Down Expand Up @@ -27,3 +28,20 @@ def get_queryset(self):
self.queryset = self.queryset.filter(author__icontains=author)

return self.queryset

@extend_schema(
parameters=[
OpenApiParameter(
name="title",
type={"type": "string", "items": {"type": "string"}},
description="Filter books by title (ex. ?title=the)"
),
OpenApiParameter(
name="author",
type={"type": "string", "items": {"type": "string"}},
description="Filter books by author (ex. ?author=Rob)"
)
]
)
def list(self, request, *args, **kwargs):
return super().list(request, args, kwargs)
8 changes: 0 additions & 8 deletions books_fixture.json
Original file line number Diff line number Diff line change
Expand Up @@ -299,14 +299,6 @@
"model": "session"
}
},
{
"model": "contenttypes.contenttype",
"pk": 7,
"fields": {
"app_label": "book_service",
"model": "book"
}
},
{
"model": "book_service.book",
"pk": 1,
Expand Down
16 changes: 14 additions & 2 deletions borrowing_service/serializers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from django.db import transaction
from django.db.models import Q
from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from borrowing_service.models import Borrowing
from payment_service.models import Payment
from payment_service.serializers import (
PaymentSerializer,
PaymentListSerializer
Expand Down Expand Up @@ -33,10 +35,20 @@ class Meta:
def create(self, validated_data):
user = validated_data.get("user")
borrowed_book = validated_data.get("book")

if borrowed_book.inventory == 0:
raise ValidationError("Sorry no books available!")
if user.borrowings.filter(is_active=True).count():
raise ValidationError("You must return your active borrowing!")

pending_payments = Payment.objects.filter(
Q(borrowing__user_id=user.id)
& Q(status=Payment.PaymentStatus.PENDING)
).count()
if pending_payments:
raise ValidationError(
"You must complete your pending payments "
"before borrowing new book!"
)

borrowed_book.inventory -= 1
borrowed_book.save()
borrowing = Borrowing.objects.create(
Expand Down
14 changes: 14 additions & 0 deletions borrowing_service/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.utils import timezone
from django.db import transaction
from drf_spectacular.utils import extend_schema, OpenApiParameter
from rest_framework import viewsets, mixins
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
Expand Down Expand Up @@ -54,11 +55,24 @@ def get_serializer_class(self):
def perform_create(self, serializer):
serializer.save(user=self.request.user)

@extend_schema(
parameters=[
OpenApiParameter(
name="is_active",
type={"type": "string", "items": {"type": "string"}},
description="Filter by borrowing status (ex. ?is_active=True)"
)
]
)
def list(self, request, *args, **kwargs):
return super().list(request, args, kwargs)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
@transaction.atomic
def borrowing_return(request, pk):
"""Endpoint for returning book"""
borrowing = Borrowing.objects.get(pk=pk)

if request.method == "POST":
Expand Down
16 changes: 10 additions & 6 deletions core/celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,16 @@
app.config_from_object(settings, namespace="CELERY")

# Celery Beat Settings
# app.conf.beat_schedule = {
# "check-overdue-task": {
# "task": "borrowing_service.tasks.check_overdue_task",
# "schedule": crontab(minute="*/1"),
# }
# }
app.conf.beat_schedule = {
"check-overdue-task": {
"task": "borrowing_service.tasks.check_overdue_task",
"schedule": crontab(minute="*/1"),
},
"check-payment-session-expiry": {
"task": "payment_service.tasks.verify_session_status",
"schedule": crontab(minute="*/1"),
},
}

# Load task modules from all registered Django apps.
app.autodiscover_tasks()
Expand Down
13 changes: 12 additions & 1 deletion core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"borrowing_service",
"payment_service",
"notifications_service"
"drf_spectacular"
]

MIDDLEWARE = [
Expand Down Expand Up @@ -143,6 +144,14 @@
"DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
),
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
}

SPECTACULAR_SETTINGS = {
"TITLE": "Your Project API",
"DESCRIPTION": "Your project description",
"VERSION": "1.0.0",
"SERVE_INCLUDE_SCHEMA": False,
}

SIMPLE_JWT = {
Expand All @@ -151,7 +160,7 @@
"ROTATE_REFRESH_TOKENS": False,
}

CELERY_BROKER_URL=os.environ["RABBIT_URL"]
CELERY_BROKER_URL = os.environ["RABBIT_URL"]

#CELERY BEAT
CELERY_BEAT_SCHEDULER = "django_celery_beat.schedulers:DatabaseScheduler"
Expand All @@ -163,3 +172,5 @@
TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN")
CHAT_ID = os.getenv("CHAT_ID")
CHAT_URL = os.getenv("CHAT_URL")
DOMAIN = os.environ["DOMAIN"]

13 changes: 12 additions & 1 deletion core/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@
"""
from django.contrib import admin
from django.urls import path, include
from drf_spectacular.views import (
SpectacularAPIView,
SpectacularSwaggerView,
SpectacularRedocView
)

urlpatterns = [
path("admin/", admin.site.urls),
Expand All @@ -30,5 +35,11 @@
path("api/users/",
include("customer.urls", namespace="customer")),
path("api/payment_service/",
include("payment_service.urls", namespace="payment_service"))
include("payment_service.urls", namespace="payment_service")),

path("api/doc/", SpectacularAPIView.as_view(), name="schema"),
path("api/doc/swagger/",
SpectacularSwaggerView.as_view(url_name="schema"), name="swagger"),
path("api/doc/redoc/",
SpectacularRedocView.as_view(url_name="schema"), name="redoc"),
]
12 changes: 7 additions & 5 deletions payment_service/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by Django 4.2.8 on 2023-12-15 14:54
# Generated by Django 4.2.8 on 2023-12-15 22:44

from django.db import migrations, models
import django.db.models.deletion
Expand All @@ -8,7 +8,7 @@ class Migration(migrations.Migration):
initial = True

dependencies = [
("borrowing_service", "0001_initial"),
("borrowing_service", "0002_initial"),
]

operations = [
Expand Down Expand Up @@ -40,11 +40,13 @@ class Migration(migrations.Migration):
max_length=40,
),
),
("session_url", models.URLField()),
("session_id", models.CharField(max_length=255)),
("session_url", models.URLField(blank=True, null=True)),
("session_id", models.CharField(blank=True, max_length=255, null=True)),
(
"money_to_be_paid",
models.DecimalField(decimal_places=2, max_digits=10000),
models.DecimalField(
blank=True, decimal_places=2, max_digits=15, null=True
),
),
(
"borrowing",
Expand Down

This file was deleted.

18 changes: 18 additions & 0 deletions payment_service/migrations/0002_alter_payment_session_url.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.0.4 on 2023-12-16 01:20

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('payment_service', '0001_initial'),
]

operations = [
migrations.AlterField(
model_name='payment',
name='session_url',
field=models.TextField(blank=True, null=True),
),
]
5 changes: 3 additions & 2 deletions payment_service/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class Payment(models.Model):
class PaymentStatus(models.TextChoices):
PENDING = "PENDING", _("pending")
PAID = "PAID", _("paid")
EXPIRED = "EXPIRED", _("expired")

class PaymentTypes(models.TextChoices):
PAYMENT = "PAYMENT", _("payment")
Expand All @@ -25,8 +26,8 @@ class PaymentTypes(models.TextChoices):
borrowing = models.ForeignKey(
Borrowing, on_delete=models.CASCADE, related_name="payments"
)
session_url = models.URLField(null=True, blank=True)
session_url = models.TextField(null=True, blank=True)
session_id = models.CharField(max_length=255, null=True, blank=True)
money_to_be_paid = models.DecimalField(
max_digits=10000, decimal_places=2, null=True, blank=True
max_digits=15, decimal_places=2, null=True, blank=True
)
1 change: 0 additions & 1 deletion payment_service/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer

from payment_service.models import Payment

Expand Down
20 changes: 12 additions & 8 deletions payment_service/services.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
from __future__ import annotations

from datetime import datetime

import stripe
from decimal import Decimal

from django.urls import reverse_lazy, reverse
from django.urls import reverse
from stripe.checkout import Session

from borrowing_service.models import Borrowing
Expand Down Expand Up @@ -39,8 +36,15 @@ def get_checkout_session(borrowing: Borrowing, payment_id: int) -> Session:
else:
payment_amount = calculate_payment_amount(borrowing)

success_url = reverse("payment_service:success", args=[payment_id])
cancel_url = reverse("payment_service:cancel", args=[payment_id])
success_url = reverse(
"payment_service:payments-payment-successful",
args=[payment_id]
)
cancel_url = reverse(
"payment_service:payments-payment-canceled",
args=[payment_id]
)
stripe.api_key = settings.STRIPE_SECRET_KEY
return Session.create(
payment_method_types=["card"],
line_items=[
Expand All @@ -54,8 +58,8 @@ def get_checkout_session(borrowing: Borrowing, payment_id: int) -> Session:
},
],
mode="payment",
success_url=success_url,
cancel_url=cancel_url,
success_url=settings.DOMAIN + success_url,
cancel_url=settings.DOMAIN + cancel_url,
)


Expand Down
24 changes: 24 additions & 0 deletions payment_service/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import stripe
from celery import shared_task

from payment_service.models import Payment


def check_if_session_expired(session_id: str) -> bool:
"""Check is session status is expired"""
session = stripe.checkout.Session.retrieve(session_id)
status = session.get("payment_intent", {}).get("status")
if status == "expired":
return True
return False


@shared_task
def verify_session_status() -> None:
"""Verify if pending sessions did not expire"""
payments = Payment.objects.filter(status=Payment.PaymentStatus.PENDING)

for payment in payments:
if check_if_session_expired(payment.session_id):
payment.status = Payment.PaymentStatus.EXPIRED
payment.save()
Loading

0 comments on commit f5da51c

Please sign in to comment.