chidioguejiofor/airtech-api

View on GitHub
airtech_api/booking/views.py

Summary

Maintainability
A
0 mins
Test Coverage
from django.http import HttpResponseRedirect
from rest_framework.views import APIView
from rest_framework.status import HTTP_201_CREATED, HTTP_409_CONFLICT
import requests
import os
from django.http import QueryDict
import json
from django.core.validators import URLValidator
from ..utils.helpers.json_helpers import (generate_response, raise_error,
                                          parse_paginator_request_query,
                                          generate_pagination_meta)

from .serializers import BookingSerializer
from ..utils.error_messages import serialization_errors
from ..utils.validators.token_validator import TokenValidator
from ..utils import success_messages
from ..flight.models import Flight
from ..utils.constants import PAYSTACK_INITIALIZE_URL
from .models import Booking


class BookingView(APIView):

    permission_classes = [TokenValidator]
    protected_methods = ['POST', 'GET']

    def post(self, request, **kwargs):
        flight_id = kwargs.get('flight_id', '')
        flight = Flight.get_model_by_id_or_404(flight_id)
        request_body = {
            'ticket_price': flight.current_price,
            'flight_model': flight.id,
            'created_by': request.decoded_user.id,
        }

        serializer = BookingSerializer(data=request_body)

        if serializer.is_valid(raise_exception=False):

            serializer.save()
            return generate_response(success_messages['booking_success'],
                                     serializer.data,
                                     status_code=HTTP_201_CREATED)
        if 'non_field_errors' in serializer.errors:

            raise_error(serialization_errors['user_book_flight_twice'],
                        status_code=HTTP_409_CONFLICT)

        raise_error(serializer.errors['flight_model'][0])


class SingleUserBookings(APIView):
    permission_classes = [TokenValidator]
    protected_methods = ['DELETE', 'GET']

    def delete(self, request, **kwargs):
        id = kwargs.get('id', '')
        extra_filter = {'created_by': request.decoded_user}
        booking = Booking.get_model_by_id_or_404(id,
                                                 extra_filters=extra_filter)
        if booking.paid_at:
            raise_error(
                serialization_errors['paid_booking_cannot_be_deleted'], )
        if booking.has_expired():
            raise_error(
                serialization_errors['cannot_delete_expired_booking'], )
        booking_id = str(booking.id)
        booking.delete()

        return generate_response(
            success_messages['deleted'].format('Booking', booking_id), )


class UserBookings(APIView):
    permission_classes = [TokenValidator]
    protected_methods = ['GET']

    def get(self, request):
        queryset = Booking.objects.filter(
            created_by=request.decoded_user.id).order_by('-created_at')

        paginator, page = parse_paginator_request_query(
            request.query_params, queryset)

        meta, current_page_data = generate_pagination_meta(paginator, page)
        serialized = BookingSerializer(current_page_data, many=True)
        serialized.child.fields.pop('bookedBy')
        paginated_data = serialized.data

        paginated_response = generate_response(
            success_messages['retrieved'].format('Bookings'),
            paginated_data,
            meta=meta)

        return paginated_response


class UserPayment(APIView):
    permission_classes = [TokenValidator]
    protected_methods = ['POST']

    def get(self, request, id):
        reference = request.query_params['reference']
        response = None
        try:
            response = requests.get(
                f'https://api.paystack.co/transaction/verify/{reference}',
                headers={
                    "Authorization":
                    "Bearer {}".format(os.getenv('PAYSTACK_SECRET'))
                })
        except Exception:
            raise_error(serialization_errors['paystack_threw_error'])

        payment_details = response.json()
        payment_data = payment_details['data']
        metadata = payment_data['metadata']
        callback_url = metadata.get("callbackURL", "/")
        if not payment_details['status']:
            redirect_url = self.generate_redirect_url(
                callback_url,
                success='false',
                bookingId=metadata['bookingId'],
                message=payment_details.get('message', 'An error occured'))
        elif payment_data['status'] != 'success':
            redirect_url = self.generate_redirect_url(
                callback_url,
                success='false',
                bookingId=metadata['bookingId'],
                message=payment_data['gateway_response'])
        else:
            booking = Booking.get_model_by_id_or_404(metadata['bookingId'])
            booking.paid_at = payment_data['paid_at']
            booking.save()
            redirect_url = self.generate_redirect_url(
                callback_url,
                success='true',
                bookingId=metadata['bookingId'],
                message=payment_data.get('gateway_response',
                                         'Payment Successful'))
        return HttpResponseRedirect(redirect_to=redirect_url, status=303)

    @staticmethod
    def generate_redirect_url(callback_url, *args, **kwargs):
        q = QueryDict(mutable=True)
        for query_string, value in kwargs.items():
            q[query_string] = value
        query_string = q.urlencode()
        return f'{callback_url}?{query_string}'

    @staticmethod
    def post(request, id):
        user = request.decoded_user

        filter_args = dict(created_by=user.id)
        booking = Booking.get_model_by_id_or_404(id, filter_args)

        if booking.paid_at:
            raise_error(serialization_errors['booking_already_paid'])

        if booking.has_expired():
            raise_error(serialization_errors['booking_expired'])

        callback_url = request.data.get('callbackURL')
        try:
            validate = URLValidator(schemes=['http', 'https'])
            validate(callback_url)
        except Exception as e:
            raise_error(
                'The `callbackURL` field must be a valid URL with protocols `http` or `https`'
            )

        try:
            response = requests.post(
                PAYSTACK_INITIALIZE_URL,
                data={
                    'amount':
                    booking.ticket_price,
                    'email':
                    user.email,
                    'metadata':
                    json.dumps({
                        'bookingId': str(booking.id),
                        'username': user.username,
                        'email': user.email,
                        'callbackURL': callback_url,
                    }),
                    'callback_url':
                    f'http://{request.get_host()}/api/v1/user/bookings/{id}/payment'
                },
                headers={
                    "Authorization":
                    "Bearer {}".format(os.getenv('PAYSTACK_SECRET'))
                })
            data = response.json()['data']
            return generate_response(
                success_messages['payment_url_created'],
                {'paymentLink': data['authorization_url']},
                200,
            )
        except Exception:
            raise_error(serialization_errors['payment_link_error'])