aboutsummaryrefslogblamecommitdiffstats
path: root/alphabetlearning/payments/views.py
blob: cdfafe6cac0c4a8c58ca063ce43dba1104567143 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
             


                                                                           

                                                         



                                                                
                             
                                                    
                                                         
 
                                        
                                                 
 


                                                                        
                                


                                                          
                                          
                                     


                                                          



                                                                                                          

                                                
 

                                           
 


                                                        

                                                    
                                                         

                      


                                       






                                                                    



                                                                                                
             
                                                                                             
                                                                                       
             
                                                                                       
                                                                                       




                                                                                    
                                                                                  



                                                                               





                                                                                                                           






                                                                                     





                                                                                                                           
         

                
 

                                 
                                                                                            
 
                              
                                                                       
 








                                   
                    


                                                                                                                                         


                                
                                                                    
 
                                          
                                                                   
 
 
                                 




                                                                  


                                             
                                                         


                                                                   


                                                          

                                                    
                           

                                                      















                                                       

                                                        
                                                                                
                                                                





                                                          
                                      
                                      
                                                                                                    
         

                                                                                     
                             
                    
                                           
 
 


                                                                         
                                                      

                                                                      
                                                                
 
 



                                                                           

















                                                                                




















                                                                                                   





                                                                                                      


                                                                            
                                            
                  




                                                        
         
                                   
 
 


                                                      
 



                                                                             
                               




                                                        
 
                            
                                                       
import stripe
from alphabetlearning.payments.models import EmailSignup, EmailVerification
from alphabetlearning.resources.models import Resource
from alphabetlearning.users.models import User
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.core.mail import mail_admins, send_mail
from django.http import HttpResponse, HttpResponseBadRequest
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse, reverse_lazy
from django.views import View
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import DeleteView, TemplateView

from .forms import EmailVerificationForm
from .models import CartItem, Price, ShoppingCart

# TODO get the cart integrated with Stripe
# Steps to convert our Cart into something that can be used with Stripe:
#
#    - Fix the total in the cart
#    - X Sort out the webhook
#    - Associate the purchases with the users profile page
#    - We need a profile page!
#        - Link in navbar (when logged in)
#    - Fix the email and make it nice
#    - Associate each of our resources with a Product item
#        - this should be done in the create resource page
#        - or we can do it manually for the time being
#    - X Check that we can add resources to the cart
#    - X When the user hits the cart page, each associated product is pulled from the database
#    - X prices are extracted from the products accordingly
#    - X use the form from the landingpage.html as the checkout button to call the create-checkout-session
#    - Add delete buttons the checkout page, etc


stripe.api_key = settings.STRIPE_SECRET_KEY


class SuccessEmailSignupView(TemplateView):
    template_name = "payments/success_email_signup.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["email"] = self.request.POST.get("email")
        return context


def email_signup_verification(request):
    if request.method == "POST":
        form = EmailVerificationForm(request.POST)
        if form.is_valid():
            # Create pending verification
            pending_verification = EmailVerification.objects.create(
                email=form.cleaned_data.get("email"),
            )
            # Generate verification URL
            verification_url: str = request.build_absolute_uri(
                reverse(
                    "payments:verify_email", args=[str(pending_verification.verification_token)]
                )
            )
            email = send_verification_email(form.cleaned_data.get("email"), verification_url)
            return render(request, "payments/verification_sent.html", {"email": email})
        else:
            email = send_verification_email(email=request.POST.get("email"), warn=True)
            return render(request, "payments/verification_sent.html", {"email": email})
    else:
        form = EmailVerificationForm()
    return render(request, "pages/home.html", {"form": form})  # Adjust as necessary


def send_verification_email(email: str, verification_url: str = None, warn=False):
    if warn:
        warning_message = """
        You are already subscribed to our list - no further action is required.
        """
        send_mail(
            subject="Alphabet Learning - Email Verification",
            message=f"Thanks, {email} for signing up.\n\n{warning_message}\n\nBest regards,\n\nThe Alphabet Learning Team",
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=[email],
            fail_silently=False,
        )
    else:
        warning_message = f"""
        Please click the following link to verify your email address within 24 hours:
        {verification_url}
        If you didn't request this, please ignore this email.
        """
        send_mail(
            subject="Alphabet Learning - Email Verification",
            message=f"Thanks, {email} for signing up.\n\n{warning_message}\n\nBest regards,\n\nThe Alphabet Learning Team",
            from_email=settings.DEFAULT_FROM_EMAIL,
            recipient_list=[email],
            fail_silently=False,
        )
    return email


def verify_email(request, token):
    try:
        pending = EmailVerification.objects.get(verification_token=token, is_verified=False)

        if pending.is_expired:
            return render(request, "payments/verification_failed.html")

        # Create the subscriber
        EmailSignup.objects.create(
            email=pending.email,
        )

        # Mark as verified
        pending.is_verified = True
        pending.save()

        mail_admins(
            subject=f"{pending.email} has just signed up to the Alphabet Learning contact list.",
            message=f"{pending.email} has just signed up to the Alphabet Learning contact list. Their verification was successful.\n\n\n"
            f"Best regards,\n\nThe Alphabet Learning Server",
            fail_silently=False,
        )

        return render(request, "payments/success_email_signup.html")

    except EmailVerification.DoesNotExist:
        return render(request, "payments/verification_failed.html")


def create_line_items(resources):
    price_objs = [r.price_obj.first() for r in resources]
    stripe_price_ids = [p.stripe_price_id for p in price_objs]
    return [{"quantity": 1, "price": s} for s in stripe_price_ids]
    pass


class CreateCheckoutSessionView(View):
    def post(self, request, *args, **kwargs):
        # price = Price.objects.get(id=self.kwargs["pk"])
        cart = ShoppingCart.objects.get(id=self.kwargs["pk"])
        resources = [i.resource for i in cart.items.all()]
        total = sum([r.price_obj.first().price for r in resources])
        domain = "http://localhost:8000"
        checkout_session = stripe.checkout.Session.create(
            payment_method_types=["card"],
            customer_email=request.user.email,
            line_items=create_line_items(resources),
            mode="payment",
            success_url=domain + "/payments/success/",
            cancel_url=domain + "/payments/cancel/",
        )
        return redirect(checkout_session.url, code=303)


class SuccessView(TemplateView):
    template_name = "payments/success.html"


class CancelView(TemplateView):
    template_name = "payments/cancel.html"


class ProductLandingPageView(TemplateView):
    template_name = "payments/landingpage.html"

    def get_context_data(self, **kwargs):
        resource = Resource.objects.get(id=91)
        prices = Price.objects.filter(resource=resource)
        context = super(ProductLandingPageView, self).get_context_data(**kwargs)
        context.update({"resource": resource, "prices": prices})
        return context


@login_required
def add_to_cart(request, resource_id):
    resource = get_object_or_404(Resource, id=resource_id)
    if not resource.price_obj.first():
        return HttpResponseBadRequest(
            "There is no price assigned to this resource. Please contact Alphabet Learning Support."
        )
    cart, created = ShoppingCart.objects.get_or_create(user=request.user)
    cart_item, created = CartItem.objects.get_or_create(cart=cart, resource=resource)
    # cart_item.quantity += 1
    cart_item.save()
    return redirect("payments:cart_detail")


@login_required
def cart_detail(request):
    cart, created = ShoppingCart.objects.get_or_create(user=request.user)
    resources = [i.resource for i in cart.items.all()]
    total = sum([r.price_obj.first().price for r in resources]) / 1000
    context = {"cart": cart, "resources": resources, "total": total}
    return render(request, "payments/cart_detail.html", context)


# def cart_detail(request):
#     cart, created = ShoppingCart.objects.get_or_create(user=request.user)
#     return render(request, "payments/cart_detail.html", {"cart": cart})


@login_required
def checkout(request):
    cart = ShoppingCart.objects.get(user=request.user)
    total = sum(item.get_total_price() for item in cart.items.all())

    if request.method == "POST":
        # Create Stripe PaymentIntent
        intent = stripe.PaymentIntent.create(
            amount=int(total * 100),  # Stripe amount is in cents
            currency="usd",
            automatic_payment_methods={"enabled": True},
        )

        # Redirect to Stripe checkout or handle payment confirmation
        return render(request, "cart/checkout_success.html")

    return render(request, "cart/checkout.html", {"cart": cart, "total": total})


@csrf_exempt
def stripe_webhook(request):
    payload = request.body
    sig_header = request.META["HTTP_STRIPE_SIGNATURE"]
    event = None

    # verify that the request came from STRIPE
    try:
        event = stripe.Webhook.construct_event(payload, sig_header, settings.STRIPE_WEBHOOK_SECRET)
    except ValueError as e:
        return HttpResponse(status=400)
    except stripe.error.SignatureVerificationError as e:
        return HttpResponse(status=400)

    if event["type"] == "checkout.session.completed":
        session = event["data"]["object"]
        customer_email = session["customer_details"]["email"]
        payment_intent = session["payment_intent"]

        # TODO Add the items to the users profile page
        # This should be done before deleting the cart otherwise we have no record of what they bought

        # TODO clear their shopping cart - we need to obtain their user ID
        user = User.objects.get(email=customer_email)
        ShoppingCart.objects.get(user=user).delete()

        # TODO add the transaction to our local history? Or just use Stripe?

        # TODO send an email to the customer
        send_mail(
            "Thank you for your purchase",
            "You have bought something nice - enjoy it",
            settings.DEFAULT_FROM_EMAIL,
            [customer_email],
            fail_silently=False,
        )
    return HttpResponse(status=200)


class DeleteCartItem(DeleteView):
    model = CartItem
    success_url = reverse_lazy("payments:cart_detail")

    # delete the cart item if there is more than one
    # delete the item and the cart itself if it is the last thing in the cart
    def post(self, request, *args, **kwargs):
        cart_items = request.user.shoppingcart.items.all()
        if len(cart_items) > 1:
            return self.delete(request, *args, **kwargs)
        else:
            request.user.shoppingcart.delete()
            return redirect("resources:resource_list")


def privacy_policy(request):
    return render(request, "pages/privacy_policy.html")