Skip to content

Commit

Permalink
Use DRF for API
Browse files Browse the repository at this point in the history
  • Loading branch information
Ivan committed Apr 5, 2023
1 parent b2b53f9 commit 6be4531
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 42 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.

This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.0.35] - 2023-04-06

### Improved

- Code base [@AivGitHub](https://github.com/AivGitHub/).

## [0.0.34] - 2023-04-4

### Added
Expand Down
22 changes: 22 additions & 0 deletions api/authentications.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from django.conf import settings
from rest_framework.authentication import BaseAuthentication

from api.exceptions import AuthenticationFailedException
from payments.core import stripe


class StripeAuthentication(BaseAuthentication):
def authenticate(self, request):
try:
signature = request.headers['Stripe-Signature']
except KeyError:
raise AuthenticationFailedException('SSO header is missing')
try:
event: stripe.Event = stripe.Webhook.construct_event(request.body, signature,
settings.STRIPE_ENDPOINT_SECRET)
except ValueError as e:
raise AuthenticationFailedException()
except stripe.error.SignatureVerificationError:
raise AuthenticationFailedException()

return None, event
13 changes: 13 additions & 0 deletions api/exception_handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from datetime import datetime

from rest_framework.views import exception_handler


def api_exception_handler(exc, context):
response = exception_handler(exc, context)
if response is not None:
response.data['message'] = response.data['detail']
response.data['time'] = datetime.now()
del response.data['detail']

return response
19 changes: 19 additions & 0 deletions api/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from rest_framework import status
from rest_framework.exceptions import APIException


class BaseCustomException(APIException):
detail = None
status_code = None

def __init__(self, detail=None, code=None):
super().__init__(detail=detail, code=code)
self.detail = detail
self.status_code = code


class AuthenticationFailedException(BaseCustomException):
def __init__(self, detail=None):
if detail is None:
detail = 'Not authenticated'
super().__init__(detail=detail, code=status.HTTP_401_UNAUTHORIZED)
26 changes: 26 additions & 0 deletions api/v1/services.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from payments.core import stripe
from payments.models import get_payment_instance, Subscription


class StripeWebhookService:
def __init__(self, event: stripe.Event):
self.event = event

def procces_post_request(self):
if self.event.type == 'checkout.session.completed':
self.checkout_session_completed()
elif self.event.type == 'invoice.payment_succeeded':
self.invoice_payment_succeeded()
elif self.event.type == 'customer.subscription.updated':
self.customer_subscription_updated()

def customer_subscription_updated(self):
payment_instance = Subscription.objects.get(psp_id=self.event.data.object.id)
payment_instance.update_from_event(self.event)

def checkout_session_completed(self):
payment_instance = get_payment_instance(self.event)
payment_instance.from_event(self.event, save=True)

def invoice_payment_succeeded(self):
pass
53 changes: 11 additions & 42 deletions api/v1/views.py
Original file line number Diff line number Diff line change
@@ -1,50 +1,19 @@
from http import HTTPStatus

from django.conf import settings
from django.http import HttpResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

from payments.core import stripe
from payments.models import get_payment_instance, Subscription
from api.authentications import StripeAuthentication
from api.v1.services import StripeWebhookService


@method_decorator(csrf_exempt, name='dispatch')
class StripeWebhook(View):
def post(self, request, *args, **kwargs):
try:
signature = request.headers['Stripe-Signature']
except KeyError:
return HttpResponse(status=HTTPStatus.FORBIDDEN)

try:
event = stripe.Webhook.construct_event(request.body, signature, settings.STRIPE_ENDPOINT_SECRET)
except ValueError as e:
return HttpResponse(status=HTTPStatus.FORBIDDEN)
except stripe.error.SignatureVerificationError:
return HttpResponse(status=HTTPStatus.FORBIDDEN)

print(event.data.object.object)
if event.type == 'checkout.session.completed':
self.checkout_session_completed(event)
elif event.type == 'invoice.payment_succeeded':
self.invoice_payment_succeeded(event)
elif event.type == 'customer.subscription.updated':
self.customer_subscription_updated(event)
class StripeWebhook(APIView):
authentication_classes = (StripeAuthentication,)

return HttpResponse(status=HTTPStatus.OK)

@staticmethod
def customer_subscription_updated(event: stripe.Event):
payment_instance = Subscription.objects.get(psp_id=event.data.object.id)
payment_instance.update_from_event(event)

@staticmethod
def checkout_session_completed(event: stripe.Event):
payment_instance = get_payment_instance(event)
payment_instance.from_event(event, save=True)
def post(self, request, *args, **kwargs):
service = StripeWebhookService(request.auth)
service.procces_post_request()

@staticmethod
def invoice_payment_succeeded(event: stripe.Event):
pass
return Response(status=status.HTTP_200_OK)
8 changes: 8 additions & 0 deletions core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
'django.contrib.staticfiles',
# 3rd party apps
'fontawesomefree',
'rest_framework',
# Custom apps
'accounts',
'base',
Expand Down Expand Up @@ -224,3 +225,10 @@
STRIPE_PUBLIC_KEY = ENV.get_value('STRIPE_PUBLIC_KEY')
STRIPE_SECRET_KEY = ENV.get_value('STRIPE_SECRET_KEY')
STRIPE_ENDPOINT_SECRET = ENV.get_value('STRIPE_ENDPOINT_SECRET')

REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'api.exception_handlers.api_exception_handler',
'DEFAULT_RENDERER_CLASSES': (
'rest_framework.renderers.JSONRenderer',
),
}
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ currencies==2020.12.12
Django==4.1.3
django-environ==0.9.0
django-storages==1.13.1
djangorestframework==3.14.0
fontawesomefree==6.3.0
googleapis-common-protos==1.57.0
grpcio==1.51.1
Expand Down

0 comments on commit 6be4531

Please sign in to comment.